3

我有一个 WritableBitmap,我想获得它的尺寸。因为该对象是由另一个线程拥有的,所以我们必须通过 Dispatcher。我试过这个:

int targetPixelWidth = 0;
int targetPixelHeight = 0;

writeableBitmap.Dispatcher.Invoke(new Action(() =>
{
    targetPixelWidth = writeableBitmap.PixelWidth;
    targetPixelHeight = writeableBitmap.PixelHeight;
}));

// Do something with targetPixelWidth and targetPixelHeight

但是,这有时会失败:即使实际值不同,这些值通常仍为 0。

认为这可能是线程问题,我将代码更改如下:

var bitmapInfo = (Tuple<int, int>)writeableBitmap.Dispatcher.Invoke(new Func<Tuple<int, int>>(
   () => Tuple.Create(writeableBitmap.PixelWidth, writeableBitmap.PixelHeight)
));

Debug.Assert(bitmapInfo != null, "Obviously, this should pass.");

targetPixelWidth = bitmapInfo.Item1;
targetPixelHeight = bitmapInfo.Item2;

// Do something with targetPixelWidth and targetPixelHeight

但现在,bitmapInfo有时为空。这很奇怪,因为(根据文档)Invoke应该只在委托没有返回值时返回 null,这在这种情况下显然是这样做的。我什至已经Debug.Assert编辑了 的返回值Tuple.Create,它永远不会为空。

我在这里想念什么?是什么导致了这种竞争状况,我该怎么办?

4

2 回答 2

0

编辑

很抱歉给出错误的答案。

似乎您想从另一个线程获取位于 gui 线程中的 WriteableBitmap 的依赖属性。

你可以试试这个:

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        var writeableBitmap = new WriteableBitmap(100, 100, 300, 300, PixelFormats.Bgra32, null);
        _image.Source = writeableBitmap;

        new Thread(() =>
            {
                Thread.Sleep(1000);

                var pixelHeigth = (Int32)writeableBitmap.Dispatcher.Invoke(
                                                     DispatcherPriority.Background,
                                                    (DispatcherOperationCallback)(arg => ((WriteableBitmap)arg).PixelHeight), writeableBitmap);
                Debug.Print("PixelHeight:" + pixelHeigth);

            }).Start();

    }

我刚试过,效果很好。

于 2013-05-01T13:49:16.627 回答
0

这有效,虽然我不知道为什么:

ManualResetEventSlim mre = new ManualResetEventSlim(false);

int targetPixelWidth = 0;
int targetPixelHeight = 0;

writeableBitmap.Dispatcher.Invoke(new Action(() =>
{
    try {
        targetPixelWidth = writeableBitmap.PixelWidth;
        targetPixelHeight = writeableBitmap.PixelHeight;
    }
    finally {
        mre.Set();
    }
}));

mre.Wait();
// Do something with targetPixelWidth and targetPixelHeight

有人(发布了这个问题的答案但后来将其删除)建议Invoke在 GUI 线程上同步,而不是在调用Invoke. 如果这是真的,那就可以解释为什么会这样。然而,试图重现此问题的文档、书籍 [ 1、2 ]和小玩具程序都表明情况并非如此。Invoke应该在调用线程上同步。

我还没能想出一个真正的解释;如果有人有一个,我会全神贯注:)。

编辑用更连贯的东西替换了我原来的,有点漫无边际的答案。

于 2013-05-02T14:06:58.530 回答