【问题标题】:WPF BitmapImage loaded from Base64String not showing从 Base64String 加载的 WPF BitmapImage 未显示
【发布时间】:2021-03-12 17:48:18
【问题描述】:

我正在尝试制作屏幕共享应用程序。作为第一步,我尝试使用屏幕截图共享主机的屏幕。 为了将主机与客户端连接,我使用 SignalR。身份验证完成后,我正在为主机启动一个计时器,该计时器将在它过去时截取屏幕截图。截图代码如下:

public static byte[] TakeScreenshot(int width = 0, int height = 0)
    {
        var bmp = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
                           Screen.PrimaryScreen.Bounds.Height,
                           PixelFormat.Format32bppArgb);

        // Create a graphics object from the bitmap.
        var gfxScreenshot = Graphics.FromImage(bmp);

        // Take the screenshot from the upper left corner to the right bottom corner.
        gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X,
                                    Screen.PrimaryScreen.Bounds.Y,
                                    0,
                                    0,
                                    Screen.PrimaryScreen.Bounds.Size,
                                    CopyPixelOperation.SourceCopy);

        if (width != 0 && height != 0)
            bmp = new Bitmap(bmp, new Size(width, height));

        using (var stream = new MemoryStream())
        {
            bmp.Save(stream, ImageFormat.Jpeg);
            return stream.ToArray();
        }
    }

计时器的经过处理程序:

private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        var bytes = ImageHelper.TakeScreenshot();
        string imageString = Convert.ToBase64String(bytes);
        Communicator.Instance.Produce(new BroadcastDataComm() { DataType = DataType.Image, Data = imageString }, _connectedHost);
    }

屏幕截图将作为字节数组接收,然后转换为 Base64String 以便通过 SignalR 进行广播。身份验证完成后,我将创建一个新的 WPF 窗口(在父 Xaml.cs 类中)并显示它,然后,所有通过主机身份验证的客户端都将收到生成的消息并将 base64string 发送到新窗口:

ScreenSharingWindow _window = new ScreenSharingWindow();
    private void AuthenticateSuccess(string id)
    {
        ConnectionId = $"Connected to: {id}";
        _connectedHost = id;
        Dispatcher.Invoke(() => _window.Show());
        _timer.Start();
    }

    private void ScreenshotReceived(BroadcastDataComm ss)
    {
        if (!(ss.Data is string imgString))
            return;


        Dispatcher.Invoke(()=> _window.ImageData = imgString);
    }

最后,我在新窗口中使用这两个属性从字符串创建一个 Bitmap 对象,并在该对象上使用转换器,以便我可以将 XAML 的 Image 控件绑定到它:

    private Bitmap _image;
    public Bitmap Image
    {
        get => _image;
        set
        {
            _image = value;
            NotifyPropertyChanged();
            Image.Save(@"C:\Users\roudy\Desktop\test.jpeg", System.Drawing.Imaging.ImageFormat.Jpeg);
        }
    }

    private string _imageData;
    public string ImageData
    {
        get => _imageData;
        set
        {
            _imageData = value;
            NotifyPropertyChanged();

            var ms = new MemoryStream(Convert.FromBase64String(ImageData));
            Image = new Bitmap(ms);
        }
    }
public class ImageConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is Bitmap bmp)
            return ImageSourceFromBitmap(bmp);
        return null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    [DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool DeleteObject([In] IntPtr hObject);

    public BitmapSource ImageSourceFromBitmap(Bitmap bmp)
    {
        var handle = bmp.GetHbitmap();
        try
        {
            var ret = Imaging.CreateBitmapSourceFromHBitmap(handle, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
            return ret;
        }
        finally { DeleteObject(handle); }
    }
}

我在属性的设置器中添加了 Image.Save(...) 以确保图像按预期接收并且工作正常,并且当我检查桌面中的缩略图时,我可以看到图像正确更新。但由于某种原因,它只是拒绝在 WPF 控件中显示。我尝试从文件创建位图,它工作正常,所以绑定和转换器是正确的。

为什么控件上不显示图像? 谢谢。

编辑:为了检查绑定是否正确,我刚刚从新窗口的构造函数中创建了位图,并注释掉了父窗口设置属性的部分。父窗口中的新处理程序将是:

private void ScreenshotReceived(BroadcastDataComm ss)
    {
        if (!(ss.Data is string imgString))
            return;


        //Dispatcher.Invoke(()=> _window.ImageData = imgString);
    }

以及新窗口的构造函数:

public ScreenSharingWindow()
    {
        InitializeComponent();
        DataContext = this;
        Image = new Bitmap(@"C:\Users\roudy\Desktop\SaveIcon.png");
    }

注意:当我尝试从“ImageData”属性中的文件加载位图时,它也不起作用,所以它似乎是一个线程问题,当它从父窗口更改时(通过 SignalR 处理程序)它没有不显示。

【问题讨论】:

  • 添加显示从文件加载位图时您所做的代码,以便社区可以比较实现。
  • 这完全令人困惑。您最终在哪里从 base64 字符串加载 BitmapImage?当您在从 base64 字符串转换的字节数组上获得 MemoryStream 时,您可以直接将其传递给 BitmapImage 的 StreamSource 属性。
  • @Clemens 很抱歉,如果不清楚,这是我在这里的第一篇文章。我正在使用 byte[]->MemoryStream-> Bitmap 将字符串转换为位图,然后使用转换器将图像的源绑定到位图。我使用了来自“gdi32.dll”的转换器
  • @Clemens 我想我已经尝试过你的方法,老实说,我已经尝试了几十种方法,但都没有奏效。无论如何,谢谢你指出,我明天早上再试一次,看看是否有帮助。

标签: c# .net wpf signalr


【解决方案1】:

问题来自 SignalR。我正在运行同一个项目的 2 个实例,屏幕截图被发送到主机而不是客户端,客户端需要它来创建一个新窗口,这导致图像为空白。感谢@Clemens 让调试变得更容易,而无需转换器和更多复杂性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多