WPF之海康面阵相机SDK的使用-实现开始采集

WPF之海康面阵相机SDK的使用-实现开始采集

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

            <Button x:Name="btn_StartGrab" Content="开始采集" Margin="10" Click="btn_StartGrab_Click"></Button>

1.是否是彩色相机

        // 判断是否为彩色图像
        private Boolean IsColorData(MyCamera.MvGvspPixelType enGvspPixelType)
        {
            switch (enGvspPixelType)
            {
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR8:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG8:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB8:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG8:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR10:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG10:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB10:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG10:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR12:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG12:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB12:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG12:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR10_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG10_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB10_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG10_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR12_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG12_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB12_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG12_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_YUV422_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_YUV422_YUYV_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_YCBCR411_8_CBYYCRYY:
                    return true;
                default:
                    return false;
            }
        }

2.转彩色图像

           /// <summary>
        /// 转换为RGB格式
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="pSrc"></param>
        /// <param name="nHeight"></param>
        /// <param name="nWidth"></param>
        /// <param name="nPixelType"></param>
        /// <param name="pDst"></param>
        /// <returns></returns>
        private Int32 ConvertToRGB(object obj, IntPtr pSrc, ushort nHeight, ushort nWidth, MyCamera.MvGvspPixelType nPixelType, IntPtr pDst)
        {
            if (IntPtr.Zero == pSrc || IntPtr.Zero == pDst)
            {
                return MyCamera.MV_E_PARAMETER;
            }

            int nRet = MyCamera.MV_OK;
            MyCamera device = obj as MyCamera;
            MyCamera.MV_PIXEL_CONVERT_PARAM stPixelConvertParam = new MyCamera.MV_PIXEL_CONVERT_PARAM();

            stPixelConvertParam.pSrcData = pSrc;//源数据
            if (IntPtr.Zero == stPixelConvertParam.pSrcData)
            {
                return -1;
            }

            stPixelConvertParam.nWidth = nWidth;//图像宽度
            stPixelConvertParam.nHeight = nHeight;//图像高度
            stPixelConvertParam.enSrcPixelType = nPixelType;//源数据的格式
            stPixelConvertParam.nSrcDataLen = (uint)(nWidth * nHeight * ((((uint)nPixelType) >> 16) & 0x00ff) >> 3);

            stPixelConvertParam.nDstBufferSize = (uint)(nWidth * nHeight * ((((uint)MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed) >> 16) & 0x00ff) >> 3);
            stPixelConvertParam.pDstBuffer = pDst;//转换后的数据
            stPixelConvertParam.enDstPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed;
            stPixelConvertParam.nDstBufferSize = (uint)nWidth * nHeight * 3;

            nRet = device.MV_CC_ConvertPixelType_NET(ref stPixelConvertParam);//格式转换
            if (MyCamera.MV_OK != nRet)
            {
                return -1;
            }

            return MyCamera.MV_OK;
        }


3.是否是黑白相机

      // 判断是否为黑白图像
      private Boolean IsMonoData(MyCamera.MvGvspPixelType enGvspPixelType)
      {
          switch (enGvspPixelType)
          {
              case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8:
              case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono10:
              case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono10_Packed:
              case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono12:
              case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono12_Packed:
                  return true;

              default:
                  return false;
          }
      }

4.转灰度图像

     
/// <summary>
/// 转换为Mono8
/// </summary>
/// <param name="obj"></param>
/// <param name="pInData"></param>
/// <param name="pOutData"></param>
/// <param name="nHeight"></param>
/// <param name="nWidth"></param>
/// <param name="nPixelType"></param>
/// <returns></returns>
private Int32 ConvertToMono8(object obj, IntPtr pInData, IntPtr pOutData, ushort nHeight, ushort nWidth, MyCamera.MvGvspPixelType nPixelType)
{
    if (IntPtr.Zero == pInData || IntPtr.Zero == pOutData)
    {
        return MyCamera.MV_E_PARAMETER;
    }

    int nRet = MyCamera.MV_OK;
    MyCamera device = obj as MyCamera;
    MyCamera.MV_PIXEL_CONVERT_PARAM stPixelConvertParam = new MyCamera.MV_PIXEL_CONVERT_PARAM();

    stPixelConvertParam.pSrcData = pInData;//源数据
    if (IntPtr.Zero == stPixelConvertParam.pSrcData)
    {
        return -1;
    }

    stPixelConvertParam.nWidth = nWidth;//图像宽度
    stPixelConvertParam.nHeight = nHeight;//图像高度
    stPixelConvertParam.enSrcPixelType = nPixelType;//源数据的格式
    stPixelConvertParam.nSrcDataLen = (uint)(nWidth * nHeight * ((((uint)nPixelType) >> 16) & 0x00ff) >> 3);

    stPixelConvertParam.nDstBufferSize = (uint)(nWidth * nHeight * ((((uint)MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed) >> 16) & 0x00ff) >> 3);
    stPixelConvertParam.pDstBuffer = pOutData;//转换后的数据
    stPixelConvertParam.enDstPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8;
    stPixelConvertParam.nDstBufferSize = (uint)(nWidth * nHeight * 3);

    nRet = device.MV_CC_ConvertPixelType_NET(ref stPixelConvertParam);//格式转换
    if (MyCamera.MV_OK != nRet)
    {
        return -1;
    }

    return nRet;
}

5.取像线程(核心)

        /// <summary>
        /// 取像线程(核心)
        /// </summary>
        private void ReceiveThreadProcess()
        {
            // 取图线程流程

            // 1.获取单帧图像数据的有效负载大小(单位为字节),即从相机传输到客户端的每一帧图像数据(不包含协议头、尾等额外开销)的实际大小
            MyCamera.MVCC_INTVALUE stParam = new MyCamera.MVCC_INTVALUE();
            int nRet = m_MyCamera.MV_CC_GetIntValue_NET("PayloadSize", ref stParam);
            if (MyCamera.MV_OK != nRet)
            {
                MessageBox.Show($"读取PayloadSize失败,失败代码:{nRet}");
                return;
            }
            UInt32 nPayloadSize = stParam.nCurValue;


            // 2.获取图像高
            nRet = m_MyCamera.MV_CC_GetIntValue_NET("Height", ref stParam);
            if (MyCamera.MV_OK != nRet)
            {
                MessageBox.Show($"获取图像高失败,失败代码:{nRet}");
                return;
            }
            uint nHeight = stParam.nCurValue;



            // 3.获取图像宽
            nRet = m_MyCamera.MV_CC_GetIntValue_NET("Width", ref stParam);
            if (MyCamera.MV_OK != nRet)
            {
                MessageBox.Show($"获取图像宽失败,失败代码:{nRet}");
                return;
            }
            uint nWidth = stParam.nCurValue;
            // 4.根据图像大小设置图像缓存
            m_pDataForRed = new byte[nWidth * nHeight];
            m_pDataForGreen = new byte[nWidth * nHeight];
            m_pDataForBlue = new byte[nWidth * nHeight];
            if (3 * nPayloadSize > m_nBufSizeForDriver)
            {
                if (m_BufForDriver != IntPtr.Zero)
                {
                    Marshal.Release(m_BufForDriver);
                }
                m_nBufSizeForDriver = 3 * nPayloadSize;
                m_BufForDriver = Marshal.AllocHGlobal((Int32)m_nBufSizeForDriver);
            }
            if (m_BufForDriver == IntPtr.Zero)
            {
                return;
            }

            IntPtr pImageBuffer = Marshal.AllocHGlobal((int)nPayloadSize * 3);
            if (pImageBuffer == IntPtr.Zero)
            {
                MessageBox.Show($"申请图像缓存区失败!");
                return;
            }

            MyCamera.MV_FRAME_OUT_INFO_EX stFrameInfo = new MyCamera.MV_FRAME_OUT_INFO_EX();
            IntPtr RedPtr = IntPtr.Zero;
            IntPtr GreenPtr = IntPtr.Zero;
            IntPtr BluePtr = IntPtr.Zero;
            IntPtr pTemp = IntPtr.Zero;
            DateTime ProStartTime = DateTime.MinValue;


            // 5.循环监听,触发相机的图像采集信号
            while (isGrabbing)
            {
                // 6.获取一帧图像数据(核心)等待,软触发或者硬触发的信号

                // 锁,实际相机只有一个,但是这个取图的线程,我们可以启动多个,不能一个线程在读取数据时候,另一个线程也对这个数据进行操作
                lock (BufForDriverLock)
                {
                    //  6.获取一帧图像数据(核心)等待,软触发或者硬触发的信号
                    nRet = m_MyCamera.MV_CC_GetOneFrameTimeout_NET(m_BufForDriver, m_nBufSizeForDriver, ref stFrameInfo, 1000);

                    // 如果采集成功,则进行记录提示
                    if (nRet == MyCamera.MV_OK)
                    {
                        ProStartTime = DateTime.Now;
                        // MessageBox.Show("相机取图完成,开始处理...");
                        m_stFrameInfo = stFrameInfo;
                    }


                    // 判别是否采集图像成功
                    if (nRet == MyCamera.MV_OK)
                    {
                        // 如果是彩色相机
                        if (IsColorData(stFrameInfo.enPixelType))
                        {
                            // 7.如果是彩色图像格式,则直接使用数据,给到pTemp
                            if (stFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed)
                            {
                                pTemp = m_BufForDriver;
                            }
                            // 否则转换成RGB三通道数据,再给到pTemp
                            else
                            {
                                nRet = ConvertToRGB(m_MyCamera, m_BufForDriver, stFrameInfo.nHeight, stFrameInfo.nWidth, stFrameInfo.enPixelType, pImageBuffer);
                                if (MyCamera.MV_OK != nRet)
                                {
                                    return;
                                }
                                pTemp = pImageBuffer;
                            }
                            // 8.获取rgb三个通道的数据
                            unsafe
                            {
                                byte* pBufForSaveImage = (byte*)pTemp;

                                UInt32 nSupWidth = (stFrameInfo.nWidth + (UInt32)3) & 0xfffffffc;//5120

                                for (int nRow = 0; nRow < stFrameInfo.nHeight; nRow++)
                                {
                                    for (int col = 0; col < stFrameInfo.nWidth; col++)
                                    {
                                        m_pDataForRed[nRow * nSupWidth + col] = pBufForSaveImage[nRow * stFrameInfo.nWidth * 3 + (3 * col)];
                                        m_pDataForGreen[nRow * nSupWidth + col] = pBufForSaveImage[nRow * stFrameInfo.nWidth * 3 + (3 * col + 1)];
                                        m_pDataForBlue[nRow * nSupWidth + col] = pBufForSaveImage[nRow * stFrameInfo.nWidth * 3 + (3 * col + 2)];
                                    }
                                }
                            }
                            RedPtr = Marshal.UnsafeAddrOfPinnedArrayElement(m_pDataForRed, 0);
                            GreenPtr = Marshal.UnsafeAddrOfPinnedArrayElement(m_pDataForGreen, 0);
                            BluePtr = Marshal.UnsafeAddrOfPinnedArrayElement(m_pDataForBlue, 0);
                            // 9.rgb三通道的指针数据转bitmap


                            //显示采集图像
                            lock (BufForImageLock)
                            {
             
                                // 进行图像显示
                                Dispatcher.Invoke(
                                    () => {

                                        int width = stFrameInfo.nWidth;
                                        int height = stFrameInfo.nHeight;

                                        m_writeableBitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Rgb24, null);
                                        m_writeableBitmap.Lock();
                                        unsafe
                                        {
                                            byte[] temp = new byte[width * height * 3];

                                            byte[] tempR = new byte[width * height];
                                            byte[] tempG = new byte[width * height];
                                            byte[] tempB = new byte[width * height];

                                            Marshal.Copy(RedPtr, tempR, 0, width * height);
                                            Marshal.Copy(GreenPtr, tempG, 0, width * height);
                                            Marshal.Copy(BluePtr, tempB, 0, width * height);

                                            for (int i = 0; i < width * height; i++)
                                            {
                                                temp[i * 3] = tempR[i];
                                                temp[i * 3 + 1] = tempG[i];
                                                temp[i * 3 + 2] = tempB[i];
                                            }

                                            Marshal.Copy(temp, 0, m_writeableBitmap.BackBuffer, width * height * 3);
                                        }
                                        m_writeableBitmap.AddDirtyRect(new Int32Rect(0, 0, width, height));
                                        m_writeableBitmap.Unlock();






                                        myImageCtr.Source = m_writeableBitmap;
                                    
                                    
                                    
                                    
                                    
                                    
                                    
                                    
                                    
                                    }
                                    );
                            


                            }
                        }

                        // 11.如果是黑白相机图像
                        else if (IsMonoData(stFrameInfo.enPixelType))
                        {
                            if (stFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8)
                            {
                                pTemp = m_BufForDriver;
                            }
                            else
                            {
                                nRet = ConvertToMono8(m_MyCamera, m_BufForDriver, pImageBuffer, stFrameInfo.nHeight, stFrameInfo.nWidth, stFrameInfo.enPixelType);
                                if (MyCamera.MV_OK != nRet)
                                {
                                    return;
                                }
                                pTemp = pImageBuffer;
                            }

                

                            // 显示采集图像
                            lock (BufForImageLock)
                            {
                                // 13.进行图像显示
                                Dispatcher.Invoke(
                                     () => {

                                         int width = stFrameInfo.nWidth;
                                         int height = stFrameInfo.nHeight;

                                         m_writeableBitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Gray8, null);

                                         // 12.灰度指针数据转writeablebitmap
                                         m_writeableBitmap.Lock();
                                         unsafe
                                         {
                                             byte[] tempR = new byte[width * height];

                                             Marshal.Copy(pTemp, tempR, 0, width * height);

                                             Marshal.Copy(tempR, 0, m_writeableBitmap.BackBuffer, width * height);
                                         }
                                         m_writeableBitmap.AddDirtyRect(new Int32Rect(0, 0, width, height));
                                         m_writeableBitmap.Unlock();


                                         myImageCtr.Source = m_writeableBitmap;

                                     }
                                     );
                            }


                        }

                        else
                        {
                            continue;
                        }
                    }
                    else
                    {
                        // 如果是触发模式,取图失败,则睡眠5ms
                        if (isTriggerMode)
                        {
                            Thread.Sleep(5);
                        }
                    }

                }






            }





        }

6.取图

        /// <summary>
        /// 开始采集
        /// </summary>
        /// <returns></returns>
        public bool StartGrab()
        {
            // 开始采集流程
            // 1.采集标志位置位,设为true,表示开始采集
            isGrabbing = true;

            // 2.启动取像线程(核心)
            m_hReceiveThread = new Thread(ReceiveThreadProcess);
            m_hReceiveThread.Start();

            // 3.取流之前先清除帧长度
            m_stFrameInfo.nFrameLen = 0;
            m_stFrameInfo.enPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG8;


            // 4.开始采集
            int nRet = m_MyCamera.MV_CC_StartGrabbing_NET();

            // 5.如果采集失败,进行提示
            if (MyCamera.MV_OK != nRet)
            {
                isGrabbing = false;
                m_hReceiveThread.Join();

                MessageBox.Show($"连续采集失败,失败代码:{nRet}");
                return false;
            }




            return true;
        }

7.开始取图

        /// <summary>
        /// 开始采集
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_StartGrab_Click(object sender, RoutedEventArgs e)
        {      // 开始采集
            if (StartGrab())
            {
                Console.WriteLine("成功开始采集");
            }

        }

完整代码

UI

<Window x:Class="WPF之海康面阵相机.MainWindow"
        xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
        xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPF之海康面阵相机"
        mc:Ignorable="d"
        Closing="Window_Closing"
        WindowStartupLocation="CenterScreen"
        Title="MainWindow" Height="600" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="2*"/>
            <ColumnDefinition Width="6*"/>
            <ColumnDefinition Width="2*"/>
        </Grid.ColumnDefinitions>
        <StackPanel Grid.Column="0">
            <Label Content="相机序列号:" Margin="10"></Label>
            <TextBox x:Name="tbx_CameraSerialNum" Text="02C93521633" Margin="10"></TextBox>
            <GroupBox Header="采集模式">
                <StackPanel>
                    <RadioButton Content="连续模式" Margin="10"></RadioButton>
                    <RadioButton Content="软触发" Margin="10"></RadioButton>
                    <RadioButton Content="硬触发" Margin="10"></RadioButton>
                </StackPanel>
            </GroupBox>

            <GroupBox Header="参数显示">
                <StackPanel>
                    <Label Content="曝光:"></Label>
                    <TextBox x:Name="tbx_Exposure" Text="0"></TextBox>
                    <Label Content="增益:"></Label>
                    <TextBox x:Name="tbx_Gain" Text="0"></TextBox>
                    <Label Content="帧率:" Cursor=""></Label>
                    <TextBox x:Name="tbx_FrameRate" Text="0"></TextBox>

                </StackPanel>
            </GroupBox>

        </StackPanel>
        <StackPanel  Grid.Column="1" Cursor="">
            <Image  x:Name="myImageCtr"  Height="400" Stretch="UniformToFill" ></Image>
        </StackPanel>
        <StackPanel Grid.Column="2">
            <Button x:Name="btn_Open" Click="btn_Open_Click" Content="打开设备" Margin="10"></Button>
            <Button x:Name="btn_Close" Content="关闭设备" Margin="10" Click="btn_Close_Click"></Button>
            <Button x:Name="btn_StartGrab" Content="开始采集" Margin="10" Click="btn_StartGrab_Click"></Button>
            <Button Content="停止采集" Margin="10"></Button>
            <Button Content="软触发一次" Margin="10"></Button>
            <Button Content="获取参数" Margin="10"></Button>
            <Button Content="设置参数" Margin="10"></Button>

        </StackPanel>
   
    </Grid>
</Window>

后端

using MvCamCtrl.NET;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WPF之海康面阵相机
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        /// <summary>
        /// 是否为触发模式
        /// </summary>
        public bool isTriggerMode { get; private set; }
        /// <summary>
        /// 相机图像(bipmap格式)
        /// </summary>
        WriteableBitmap m_writeableBitmap;
        // 读写图像时锁定
        private Object BufForDriverLock = new Object();
        private Object BufForImageLock = new Object();
        UInt32 m_nBufSizeForDriver = 0;
        // R通道数据
        byte[] m_pDataForRed = null;
        // G通道数据
        byte[] m_pDataForGreen = null;
        // B通道数据
        byte[] m_pDataForBlue = null;

        // 用于从驱动获取图像的缓存
        IntPtr m_BufForDriver;

        // 取像线程
        Thread m_hReceiveThread = null;
        // 设备列表
        private MyCamera.MV_CC_DEVICE_INFO_LIST m_stDeviceList;

        // 相机对象
        private MyCamera m_MyCamera = null;

        // 帧信息
        MyCamera.MV_FRAME_OUT_INFO_EX m_stFrameInfo = new MyCamera.MV_FRAME_OUT_INFO_EX();


        /// <summary>
        /// 相机是否已连接
        /// </summary>
        public bool IsConnect { get; private set; }

        /// <summary>
        /// 是否开始采集
        /// </summary>
        public bool isGrabbing { get; private set; }
        public MainWindow()
        {
            InitializeComponent();
        }

        /// <summary>
        /// 打开相机
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Open_Click(object sender, RoutedEventArgs e)
        {
            if (OpenDevice())
            {
                MessageBox.Show("打开相机成功");
            }
        }



        /// <summary>
        /// 获取相机对应的枚举索引
        /// </summary>
        /// <param name="CameraID"></param>
        /// <returns></returns>
        private int GetDeviceIndex(string CameraID)
        {
            for (int i = 0; i < m_stDeviceList.nDeviceNum; i++)
            {
                MyCamera.MV_CC_DEVICE_INFO device = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(m_stDeviceList.pDeviceInfo[i], typeof(MyCamera.MV_CC_DEVICE_INFO));
                if (device.nTLayerType == MyCamera.MV_GIGE_DEVICE)
                {
                    MyCamera.MV_GIGE_DEVICE_INFO gigeInfo = (MyCamera.MV_GIGE_DEVICE_INFO)MyCamera.ByteToStruct(device.SpecialInfo.stGigEInfo, typeof(MyCamera.MV_GIGE_DEVICE_INFO));
                    if (gigeInfo.chSerialNumber == CameraID)
                        return i;
                }
                else if (device.nTLayerType == MyCamera.MV_USB_DEVICE)
                {
                    MyCamera.MV_USB3_DEVICE_INFO usb3Info = (MyCamera.MV_USB3_DEVICE_INFO)MyCamera.ByteToStruct(device.SpecialInfo.stUsb3VInfo, typeof(MyCamera.MV_USB3_DEVICE_INFO));
                    if (usb3Info.chSerialNumber == CameraID)
                        return i;
                }
            }

            return -1;
        }
 
        /// <summary>
        /// 枚举海康相机(GIGE,USB3)
        /// </summary>
        public void EnumDevices()
        {
            // 枚举设备列表
            m_stDeviceList.nDeviceNum = 0;
            int nRet = MyCamera.MV_CC_EnumDevices_NET(MyCamera.MV_GIGE_DEVICE | MyCamera.MV_USB_DEVICE, ref m_stDeviceList);
            if (0 != nRet)
            {
                MessageBox.Show("枚举HIK相机设备失败!");
                return;
            }
        }

        /// <summary>
        /// 异常,则关闭相机
        /// </summary>
        /// <param name="nMsgType"></param>
        /// <param name="pUser"></param>
        private void cbException(uint nMsgType, IntPtr pUser)
        {
            IsConnect = false;

            if (nMsgType == MyCamera.MV_EXCEPTION_DEV_DISCONNECT)
            {
                // 先关闭设备
                CloseDevice();

                // 在尝试重新打开设备
                if (OpenDevice())
                {
                    MessageBox.Show("尝试重新连接设备失败!");
                }
            }
        }
        /// <summary>
        /// 关闭设备
        /// </summary>
        public void CloseDevice()
        {
            // 取流标志位清零
            if (isGrabbing == true)
            {
                isGrabbing = false;
                m_hReceiveThread.Join();
            }

            if (m_BufForDriver != IntPtr.Zero)
            {
                Marshal.Release(m_BufForDriver);
            }

            // 关闭设备
            m_MyCamera.MV_CC_CloseDevice_NET();
            m_MyCamera.MV_CC_DestroyDevice_NET();

            IsConnect = false;
        }
        /// <summary>
        /// 打开相机
        /// </summary>
        /// <returns></returns>
        public bool OpenDevice()
        {
            //  打开相机流程:

            // 1.获取所有相机
            EnumDevices();

            // 2.根据相机序列号,看该序列号对应的相机ID是否存在,确定该相机是否存在
            // 获取相机索引
            int camIdx = GetDeviceIndex(tbx_CameraSerialNum.Text.Trim());
            if (camIdx == -1)
            {
                MessageBox.Show("找不到该ID的相机!");
                return false;
            }

            // 3.获取相机信息
            // 获取相机设备信息
            MyCamera.MV_CC_DEVICE_INFO device = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(m_stDeviceList.pDeviceInfo[camIdx], typeof(MyCamera.MV_CC_DEVICE_INFO));



            // 4.建立设备对象
            // 建立设备对象
            if (null == m_MyCamera)
            {
                // 创建相机实例
                m_MyCamera = new MyCamera();
                if (null == m_MyCamera)
                {
                    MessageBox.Show("初始化相机对象失败");

                    return false;
                }
            }



            // 5.根据相机信息创建相机
            int nRet = m_MyCamera.MV_CC_CreateDevice_NET(ref device);
            if (MyCamera.MV_OK != nRet)
            {
                MessageBox.Show($"创建设备失败,失败代码:{nRet}");
                return false;
            }

            // 6.打开设备
            nRet = m_MyCamera.MV_CC_OpenDevice_NET();

            // 如果打开失败,则销毁相机设备
            if (MyCamera.MV_OK != nRet)
            {
                m_MyCamera.MV_CC_DestroyDevice_NET();

                MessageBox.Show($"设备打开失败,失败代码:{nRet}");
                return false;
            }
            // 7.探测网络最佳包大小,并进行设置(只对GigE相机有效)
            if (device.nTLayerType == MyCamera.MV_GIGE_DEVICE)
            {
                // 获取最优网络包大小
                int nPacketSize = m_MyCamera.MV_CC_GetOptimalPacketSize_NET();
                if (nPacketSize > 0)
                {
                    nRet = m_MyCamera.MV_CC_SetIntValue_NET("GevSCPSPacketSize", (uint)nPacketSize);
                    if (nRet != MyCamera.MV_OK)
                    {
                        MessageBox.Show($"设置包大小失败,失败代码:{nRet}");
                    }
                }
                else
                {
                    MessageBox.Show($"获取包大小失败,返回的包大小为:{nPacketSize}");
                }
            }

            // 8.设置为连续采集模式
            m_MyCamera.MV_CC_SetEnumValue_NET("AcquisitionMode", (uint)MyCamera.MV_CAM_ACQUISITION_MODE.MV_ACQ_MODE_CONTINUOUS);
            m_MyCamera.MV_CC_SetEnumValue_NET("TriggerMode", (uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_OFF);


            // 9.注册异常回调函数
            m_MyCamera.MV_CC_RegisterExceptionCallBack_NET(cbException, IntPtr.Zero);
            return true;
        }

        /// <summary>
        /// 关闭设备
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Close_Click(object sender, RoutedEventArgs e)
        {
            // 关闭相机
            CloseDevice();
        }

        /// <summary>
        /// 开始采集
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_StartGrab_Click(object sender, RoutedEventArgs e)
        {      // 开始采集
            if (StartGrab())
            {
                Console.WriteLine("成功开始采集");
            }

        }

        /// <summary>
        /// 开始采集
        /// </summary>
        /// <returns></returns>
        public bool StartGrab()
        {
            // 开始采集流程
            // 1.采集标志位置位,设为true,表示开始采集
            isGrabbing = true;

            // 2.启动取像线程(核心)
            m_hReceiveThread = new Thread(ReceiveThreadProcess);
            m_hReceiveThread.Start();

            // 3.取流之前先清除帧长度
            m_stFrameInfo.nFrameLen = 0;
            m_stFrameInfo.enPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG8;


            // 4.开始采集
            int nRet = m_MyCamera.MV_CC_StartGrabbing_NET();

            // 5.如果采集失败,进行提示
            if (MyCamera.MV_OK != nRet)
            {
                isGrabbing = false;
                m_hReceiveThread.Join();

                MessageBox.Show($"连续采集失败,失败代码:{nRet}");
                return false;
            }




            return true;
        }

        /// <summary>
        /// 取像线程(核心)
        /// </summary>
        private void ReceiveThreadProcess()
        {
            // 取图线程流程

            // 1.获取单帧图像数据的有效负载大小(单位为字节),即从相机传输到客户端的每一帧图像数据(不包含协议头、尾等额外开销)的实际大小
            MyCamera.MVCC_INTVALUE stParam = new MyCamera.MVCC_INTVALUE();
            int nRet = m_MyCamera.MV_CC_GetIntValue_NET("PayloadSize", ref stParam);
            if (MyCamera.MV_OK != nRet)
            {
                MessageBox.Show($"读取PayloadSize失败,失败代码:{nRet}");
                return;
            }
            UInt32 nPayloadSize = stParam.nCurValue;


            // 2.获取图像高
            nRet = m_MyCamera.MV_CC_GetIntValue_NET("Height", ref stParam);
            if (MyCamera.MV_OK != nRet)
            {
                MessageBox.Show($"获取图像高失败,失败代码:{nRet}");
                return;
            }
            uint nHeight = stParam.nCurValue;



            // 3.获取图像宽
            nRet = m_MyCamera.MV_CC_GetIntValue_NET("Width", ref stParam);
            if (MyCamera.MV_OK != nRet)
            {
                MessageBox.Show($"获取图像宽失败,失败代码:{nRet}");
                return;
            }
            uint nWidth = stParam.nCurValue;
            // 4.根据图像大小设置图像缓存
            m_pDataForRed = new byte[nWidth * nHeight];
            m_pDataForGreen = new byte[nWidth * nHeight];
            m_pDataForBlue = new byte[nWidth * nHeight];
            if (3 * nPayloadSize > m_nBufSizeForDriver)
            {
                if (m_BufForDriver != IntPtr.Zero)
                {
                    Marshal.Release(m_BufForDriver);
                }
                m_nBufSizeForDriver = 3 * nPayloadSize;
                m_BufForDriver = Marshal.AllocHGlobal((Int32)m_nBufSizeForDriver);
            }
            if (m_BufForDriver == IntPtr.Zero)
            {
                return;
            }

            IntPtr pImageBuffer = Marshal.AllocHGlobal((int)nPayloadSize * 3);
            if (pImageBuffer == IntPtr.Zero)
            {
                MessageBox.Show($"申请图像缓存区失败!");
                return;
            }

            MyCamera.MV_FRAME_OUT_INFO_EX stFrameInfo = new MyCamera.MV_FRAME_OUT_INFO_EX();
            IntPtr RedPtr = IntPtr.Zero;
            IntPtr GreenPtr = IntPtr.Zero;
            IntPtr BluePtr = IntPtr.Zero;
            IntPtr pTemp = IntPtr.Zero;
            DateTime ProStartTime = DateTime.MinValue;


            // 5.循环监听,触发相机的图像采集信号
            while (isGrabbing)
            {
                // 6.获取一帧图像数据(核心)等待,软触发或者硬触发的信号

                // 锁,实际相机只有一个,但是这个取图的线程,我们可以启动多个,不能一个线程在读取数据时候,另一个线程也对这个数据进行操作
                lock (BufForDriverLock)
                {
                    //  6.获取一帧图像数据(核心)等待,软触发或者硬触发的信号
                    nRet = m_MyCamera.MV_CC_GetOneFrameTimeout_NET(m_BufForDriver, m_nBufSizeForDriver, ref stFrameInfo, 1000);

                    // 如果采集成功,则进行记录提示
                    if (nRet == MyCamera.MV_OK)
                    {
                        ProStartTime = DateTime.Now;
                        // MessageBox.Show("相机取图完成,开始处理...");
                        m_stFrameInfo = stFrameInfo;
                    }


                    // 判别是否采集图像成功
                    if (nRet == MyCamera.MV_OK)
                    {
                        // 如果是彩色相机
                        if (IsColorData(stFrameInfo.enPixelType))
                        {
                            // 7.如果是彩色图像格式,则直接使用数据,给到pTemp
                            if (stFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed)
                            {
                                pTemp = m_BufForDriver;
                            }
                            // 否则转换成RGB三通道数据,再给到pTemp
                            else
                            {
                                nRet = ConvertToRGB(m_MyCamera, m_BufForDriver, stFrameInfo.nHeight, stFrameInfo.nWidth, stFrameInfo.enPixelType, pImageBuffer);
                                if (MyCamera.MV_OK != nRet)
                                {
                                    return;
                                }
                                pTemp = pImageBuffer;
                            }
                            // 8.获取rgb三个通道的数据
                            unsafe
                            {
                                byte* pBufForSaveImage = (byte*)pTemp;

                                UInt32 nSupWidth = (stFrameInfo.nWidth + (UInt32)3) & 0xfffffffc;//5120

                                for (int nRow = 0; nRow < stFrameInfo.nHeight; nRow++)
                                {
                                    for (int col = 0; col < stFrameInfo.nWidth; col++)
                                    {
                                        m_pDataForRed[nRow * nSupWidth + col] = pBufForSaveImage[nRow * stFrameInfo.nWidth * 3 + (3 * col)];
                                        m_pDataForGreen[nRow * nSupWidth + col] = pBufForSaveImage[nRow * stFrameInfo.nWidth * 3 + (3 * col + 1)];
                                        m_pDataForBlue[nRow * nSupWidth + col] = pBufForSaveImage[nRow * stFrameInfo.nWidth * 3 + (3 * col + 2)];
                                    }
                                }
                            }
                            RedPtr = Marshal.UnsafeAddrOfPinnedArrayElement(m_pDataForRed, 0);
                            GreenPtr = Marshal.UnsafeAddrOfPinnedArrayElement(m_pDataForGreen, 0);
                            BluePtr = Marshal.UnsafeAddrOfPinnedArrayElement(m_pDataForBlue, 0);
                            // 9.rgb三通道的指针数据转bitmap


                            //显示采集图像
                            lock (BufForImageLock)
                            {
             
                                // 进行图像显示
                                Dispatcher.Invoke(
                                    () => {

                                        int width = stFrameInfo.nWidth;
                                        int height = stFrameInfo.nHeight;

                                        m_writeableBitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Rgb24, null);
                                        m_writeableBitmap.Lock();
                                        unsafe
                                        {
                                            byte[] temp = new byte[width * height * 3];

                                            byte[] tempR = new byte[width * height];
                                            byte[] tempG = new byte[width * height];
                                            byte[] tempB = new byte[width * height];

                                            Marshal.Copy(RedPtr, tempR, 0, width * height);
                                            Marshal.Copy(GreenPtr, tempG, 0, width * height);
                                            Marshal.Copy(BluePtr, tempB, 0, width * height);

                                            for (int i = 0; i < width * height; i++)
                                            {
                                                temp[i * 3] = tempR[i];
                                                temp[i * 3 + 1] = tempG[i];
                                                temp[i * 3 + 2] = tempB[i];
                                            }

                                            Marshal.Copy(temp, 0, m_writeableBitmap.BackBuffer, width * height * 3);
                                        }
                                        m_writeableBitmap.AddDirtyRect(new Int32Rect(0, 0, width, height));
                                        m_writeableBitmap.Unlock();






                                        myImageCtr.Source = m_writeableBitmap;
                                    
                                    
                                    
                                    
                                    
                                    
                                    
                                    
                                    
                                    }
                                    );
                            


                            }
                        }

                        // 11.如果是黑白相机图像
                        else if (IsMonoData(stFrameInfo.enPixelType))
                        {
                            if (stFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8)
                            {
                                pTemp = m_BufForDriver;
                            }
                            else
                            {
                                nRet = ConvertToMono8(m_MyCamera, m_BufForDriver, pImageBuffer, stFrameInfo.nHeight, stFrameInfo.nWidth, stFrameInfo.enPixelType);
                                if (MyCamera.MV_OK != nRet)
                                {
                                    return;
                                }
                                pTemp = pImageBuffer;
                            }

                

                            // 显示采集图像
                            lock (BufForImageLock)
                            {
                                // 13.进行图像显示
                                Dispatcher.Invoke(
                                     () => {

                                         int width = stFrameInfo.nWidth;
                                         int height = stFrameInfo.nHeight;

                                         m_writeableBitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Gray8, null);

                                         // 12.灰度指针数据转writeablebitmap
                                         m_writeableBitmap.Lock();
                                         unsafe
                                         {
                                             byte[] tempR = new byte[width * height];

                                             Marshal.Copy(pTemp, tempR, 0, width * height);

                                             Marshal.Copy(tempR, 0, m_writeableBitmap.BackBuffer, width * height);
                                         }
                                         m_writeableBitmap.AddDirtyRect(new Int32Rect(0, 0, width, height));
                                         m_writeableBitmap.Unlock();


                                         myImageCtr.Source = m_writeableBitmap;

                                     }
                                     );
                            }


                        }

                        else
                        {
                            continue;
                        }
                    }
                    else
                    {
                        // 如果是触发模式,取图失败,则睡眠5ms
                        if (isTriggerMode)
                        {
                            Thread.Sleep(5);
                        }
                    }

                }






            }





        }

      
        /// <summary>
        /// 判断是否为黑白图像
        /// </summary>
        /// <param name="enGvspPixelType"></param>
        /// <returns></returns>
        private Boolean IsMonoData(MyCamera.MvGvspPixelType enGvspPixelType)
        {
            switch (enGvspPixelType)
            {
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono10:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono10_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono12:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono12_Packed:
                    return true;

                default:
                    return false;
            }
        }
         
        /// <summary>
        /// 判断是否为彩色图像
        /// </summary>
        /// <param name="enGvspPixelType"></param>
        /// <returns></returns>
        private Boolean IsColorData(MyCamera.MvGvspPixelType enGvspPixelType)
        {
            switch (enGvspPixelType)
            {
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR8:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG8:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB8:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG8:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR10:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG10:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB10:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG10:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR12:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG12:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB12:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG12:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR10_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG10_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB10_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG10_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR12_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG12_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB12_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG12_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_YUV422_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_YUV422_YUYV_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_YCBCR411_8_CBYYCRYY:
                    return true;
                default:
                    return false;
            }
        }

        /// <summary>
        /// 转换为RGB格式
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="pSrc"></param>
        /// <param name="nHeight"></param>
        /// <param name="nWidth"></param>
        /// <param name="nPixelType"></param>
        /// <param name="pDst"></param>
        /// <returns></returns>
        private Int32 ConvertToRGB(object obj, IntPtr pSrc, ushort nHeight, ushort nWidth, MyCamera.MvGvspPixelType nPixelType, IntPtr pDst)
        {
            if (IntPtr.Zero == pSrc || IntPtr.Zero == pDst)
            {
                return MyCamera.MV_E_PARAMETER;
            }

            int nRet = MyCamera.MV_OK;
            MyCamera device = obj as MyCamera;
            MyCamera.MV_PIXEL_CONVERT_PARAM stPixelConvertParam = new MyCamera.MV_PIXEL_CONVERT_PARAM();

            stPixelConvertParam.pSrcData = pSrc;//源数据
            if (IntPtr.Zero == stPixelConvertParam.pSrcData)
            {
                return -1;
            }

            stPixelConvertParam.nWidth = nWidth;//图像宽度
            stPixelConvertParam.nHeight = nHeight;//图像高度
            stPixelConvertParam.enSrcPixelType = nPixelType;//源数据的格式
            stPixelConvertParam.nSrcDataLen = (uint)(nWidth * nHeight * ((((uint)nPixelType) >> 16) & 0x00ff) >> 3);

            stPixelConvertParam.nDstBufferSize = (uint)(nWidth * nHeight * ((((uint)MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed) >> 16) & 0x00ff) >> 3);
            stPixelConvertParam.pDstBuffer = pDst;//转换后的数据
            stPixelConvertParam.enDstPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed;
            stPixelConvertParam.nDstBufferSize = (uint)nWidth * nHeight * 3;

            nRet = device.MV_CC_ConvertPixelType_NET(ref stPixelConvertParam);//格式转换
            if (MyCamera.MV_OK != nRet)
            {
                return -1;
            }

            return MyCamera.MV_OK;
        }


        /// <summary>
        /// 转换为Mono8
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="pInData"></param>
        /// <param name="pOutData"></param>
        /// <param name="nHeight"></param>
        /// <param name="nWidth"></param>
        /// <param name="nPixelType"></param>
        /// <returns></returns>
        private Int32 ConvertToMono8(object obj, IntPtr pInData, IntPtr pOutData, ushort nHeight, ushort nWidth, MyCamera.MvGvspPixelType nPixelType)
        {
            if (IntPtr.Zero == pInData || IntPtr.Zero == pOutData)
            {
                return MyCamera.MV_E_PARAMETER;
            }

            int nRet = MyCamera.MV_OK;
            MyCamera device = obj as MyCamera;
            MyCamera.MV_PIXEL_CONVERT_PARAM stPixelConvertParam = new MyCamera.MV_PIXEL_CONVERT_PARAM();

            stPixelConvertParam.pSrcData = pInData;//源数据
            if (IntPtr.Zero == stPixelConvertParam.pSrcData)
            {
                return -1;
            }

            stPixelConvertParam.nWidth = nWidth;//图像宽度
            stPixelConvertParam.nHeight = nHeight;//图像高度
            stPixelConvertParam.enSrcPixelType = nPixelType;//源数据的格式
            stPixelConvertParam.nSrcDataLen = (uint)(nWidth * nHeight * ((((uint)nPixelType) >> 16) & 0x00ff) >> 3);

            stPixelConvertParam.nDstBufferSize = (uint)(nWidth * nHeight * ((((uint)MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed) >> 16) & 0x00ff) >> 3);
            stPixelConvertParam.pDstBuffer = pOutData;//转换后的数据
            stPixelConvertParam.enDstPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8;
            stPixelConvertParam.nDstBufferSize = (uint)(nWidth * nHeight * 3);

            nRet = device.MV_CC_ConvertPixelType_NET(ref stPixelConvertParam);//格式转换
            if (MyCamera.MV_OK != nRet)
            {
                return -1;
            }

            return nRet;
        }

        /// <summary>
        /// 关闭窗口
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            CloseDevice();
        }
    }
}

停止采集

      /// <summary>
      /// 停止采集
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void btn_StopGrab_Click(object sender, RoutedEventArgs e)
      {
          StopGrab();
      }
      /// <summary>
      /// 停止采集
      /// </summary>
      /// <returns></returns>
      public bool StopGrab()
      {
          // 标志位设为false
          isGrabbing = false;

          // 取像线程,停止
          m_hReceiveThread.Join();

          // 停止采集
          int nRet = m_MyCamera.MV_CC_StopGrabbing_NET();
          if (nRet != MyCamera.MV_OK)
          {
              MessageBox.Show($"停止采集失败,失败代码:{nRet}");
              return false;
          }

          return true;
      }

补充:

在WPF中,Image 控件的显示模式主要通过 StretchStretchDirection 属性控制,用于定义图像如何适应分配的空间。以下是详细说明和示例:


1. Stretch 属性

决定图像在目标空间中的缩放方式,可选值:

  • None:不缩放,保持原始尺寸。若图像大于控件,会显示部分内容。
  • Fill:拉伸图像以完全填充控件,不保持宽高比(可能变形)。
  • Uniform(默认):保持宽高比缩放,图像完全显示(可能留白)。
  • UniformToFill:保持宽高比缩放,完全填充控件(可能裁剪部分图像)。
示例代码:
<Image Source="image.jpg" Stretch="UniformToFill" />

2. StretchDirection 属性

控制缩放的方向,可选值:

  • UpOnly:仅当图像小于控件时放大。
  • DownOnly:仅当图像大于控件时缩小。
  • Both(默认):双向缩放。
示例代码:
<Image Source="image.jpg" Stretch="Uniform" StretchDirection="DownOnly" />

3. 其他相关属性

  • Width/Height:固定控件尺寸,影响图像显示区域。
  • MaxWidth/MaxHeight:限制最大尺寸,结合 Stretch 使用。
  • HorizontalAlignment/VerticalAlignment:控制图像在控件内的对齐方式(如 CenterStretch)。
示例:保持比例并居中
<Image 
    Source="image.jpg" 
    Stretch="Uniform"
    Width="200" Height="200"
    HorizontalAlignment="Center"
    VerticalAlignment="Center" />

4. 动态设置(代码后台)

// C# 代码设置
myImage.Stretch = Stretch.Uniform;
myImage.StretchDirection = StretchDirection.Both;

5. 高级场景

  • 视图框(Viewbox):用 Viewbox 包裹 Image 实现动态缩放。
    <Viewbox Stretch="Uniform">
        <Image Source="image.jpg" />
    </Viewbox>
    
  • 裁剪(Clip):通过 Clip 属性自定义显示区域。

总结

场景推荐设置
保持比例,完整显示Stretch="Uniform"
填充控件,可能裁剪Stretch="UniformToFill"
禁止变形,原始尺寸Stretch="None"
仅缩小大图StretchDirection="DownOnly"

根据需求调整这些属性,即可灵活控制图像的显示模式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值