Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 将可写位图绑定到画布_C#_Wpf - Fatal编程技术网

C# 将可写位图绑定到画布

C# 将可写位图绑定到画布,c#,wpf,C#,Wpf,我正试图编写一个简单的WPF程序来实现康威的生活游戏。 我的窗口由一个工具栏和一个画布组成,画布上有一个嵌入的图像,我试图在上面显示一个可写的位图 我在工具栏上有一个按钮,单击该按钮时,会更新一代,然后在画布图像上显示生成的位图。我直接用 img.Source = wbmp; 这是毫无问题的 但是,我希望显示正在进行的更新,而不必每次单击“更新”按钮。所以我试着实现一个10次迭代的循环。但是,更新后的图像仅在第10次迭代完成后显示(即,我看不到前9代) 我的理解是,我需要将图像控件绑定到可写位

我正试图编写一个简单的WPF程序来实现康威的生活游戏。 我的窗口由一个工具栏和一个画布组成,画布上有一个嵌入的图像,我试图在上面显示一个可写的位图

我在工具栏上有一个按钮,单击该按钮时,会更新一代,然后在画布图像上显示生成的位图。我直接用

img.Source = wbmp;
这是毫无问题的

但是,我希望显示正在进行的更新,而不必每次单击“更新”按钮。所以我试着实现一个10次迭代的循环。但是,更新后的图像仅在第10次迭代完成后显示(即,我看不到前9代)

我的理解是,我需要将图像控件绑定到可写位图,以便“强制”每代更新。我已经用下面的代码尝试过了,但是现在什么都没有显示。起初,我发现PropertyChanged事件似乎没有触发,但我没有分配任何方法,因此,我添加了PropertyChanged=delegate{};(我这么做是因为互联网让我这么做!)

我真的不知道我会错在哪里。我对WPF和绑定尤其一无所知。(我的大部分代码都是改编的。)任何帮助都将不胜感激

<Canvas Name ="canvas" Grid.Row="1" Background="LightGray">
    <Image Name="img" Source="{Binding GoLImage}"/>
</Canvas>

public class MyViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged = delegate { };
    private ImageSource golImage;  //writeable bitmap;

    public ImageSource GoLImage
    {
        get { return golImage; }
        set
        {
            golImage = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(GoLImage)));
        }
    }

}

 private void Button_Run10_Click(object sender, RoutedEventArgs e)
    {
        BitmapPixelMaker bmp = new BitmapPixelMaker(1200, 800);
        for (int i = 0; i < 10; i++)
        {
            goL.UpdateLifeGrid();  //goL is an instance of my class implementing the Game of Life
            goL.LifeGridTobmp(bmp);
            WriteableBitmap wbmp = bmp.MakeBitmap(96, 96);

            //Trying to display bitmap
            MyViewModel golDisplay = new MyViewModel();
            golDisplay.GoLImage = wbmp; //This doesn't automatically display on each iteration

        }
    }

公共类MyViewModel:INotifyPropertyChanged
{
公共事件PropertyChangedEventHandler PropertyChanged=委托{};
私有ImageSource golImage;//可写位图;
公共图像源图像
{
获取{return golImage;}
设置
{
图像=值;
PropertyChanged?.Invoke(这是新的PropertyChangedEventArgs(名称)(GoLImage));
}
}
}
私有无效按钮\u运行10\u单击(对象发送者,路由目标e)
{
BitmapPixelMaker bmp=新的BitmapPixelMaker(1200800);
对于(int i=0;i<10;i++)
{
goL.UpdateLifeGrid();//goL是我的类实现生命游戏的一个实例
goL.LifeGridTobmp(bmp);
WriteableBitmap wbmp=bmp.MakeBitmap(96,96);
//正在尝试显示位图
MyViewModel golDisplay=新的MyViewModel();
golDisplay.GoLImage=wbmp;//这不会在每次迭代中自动显示
}
}

按钮单击处理程序中的
for
循环会阻止UI线程。并且您尚未将视图模型实例指定给视图的DataContext属性

使用带有Tick事件处理程序的Dispatcher,如下所示

不要在每个周期中创建新的视图模型实例和新的WriteableBitmap,只需修改现有实例-因此,应将视图模型属性声明更改为
public WriteableBitmap GoLImage
,以便ModifyBitmap方法可以访问它

private readonly MyViewModel golDisplay = new MyViewModel();

private readonly DispatcherTimer timer = new DispatcherTimer
{
    Interval = TimeSpan.FromSeconds(0.1)
};

public MainWindow()
{
    InitializeComponent();

    golDisplay.GoLImage = WriteableBitmap(1200, 800, 96, 96, PixelFormats.Default, null);
    DataContext = golDisplay;

    timer.Tick += OnTimerTick;
    timer.Start(); // optionally, call Start/Stop in a Button Click handler
}

private void OnTimerTick(object sender, EventArgs e)
{
    var bmp = new BitmapPixelMaker(1200, 800);

    goL.UpdateLifeGrid();
    goL.LifeGridTobmp(bmp);

    ModifyBitmap(bmp); // write directly to golDisplay.GoLImage
}

Dispatcher的另一种替代方法可能是异步等待的更新调用上的简单循环

如下图所示,其中更新方法将执行CPU密集型计算,然后返回新的像素缓冲区

private async void OnWindowLoaded(object sender, RoutedEventArgs e)
{
    while (true)
    {
        var buffer = await Task.Run(() => game.Update());

        bitmap.WritePixels(new Int32Rect(0, 0, game.Width, game.Height),
                           buffer, game.Stride, 0);
    }
}

非常感谢你的回复,克莱门斯。我理解你所说的一些内容,但仍对一些事情感到困惑(由于我缺乏知识,而不是你的解释!)然而,最重要的是你的建议有效,这给了我一些东西去尝试和进一步理解。非常感谢!我仍然感到困惑,或者说更惊讶的是,将DispatchTimer与TickEvent结合使用才是正确的选择。没有办法在循环中强制更新吗?更新必须以计时器间隔指定的固定间隔进行,而不是在执行更新的计算完成时进行,这似乎“很奇怪”。渲染线程需要有机会显示场景。可以将其视为以特定帧速率运行的电影。当然,也有异步计算并在完成计算后立即更新UI的方法,但帧速率会一直变化,这取决于每个周期的计算时间。当然,您可以缩短计时器的间隔。然而,低于10毫秒的任何东西都没有意义。只是对GoL实现进行了一些研究,发现只要异步更新并立即更新WriteableBitmap,它就会运行得很好。看编辑后的答案。哈!我已经成功地得到了类似的结果。为了工作没有任何问题:-)(但我将很快发布另一个关于MouseLeftButtonUp事件的问题,该事件似乎没有触发:-)