0

我遇到了 WPF 应用程序中的图像未更新的问题。有人建议我“将方法放在窗口的调度程序调度程序中。尽可能调度到最近的元素。并且异步......”但我找不到任何如何做到这一点的例子。

我将如何调用一个方法,“从窗口的调度程序异步”?

这是在 while 循环中的代码,在 while 循环完成之前它不会更新:

writeableBitmap = new WriteableBitmap(CleanVegMap);
image.Source = writeableBitmap;
DrawDinos2d();

这是主窗口的 XAML:

<Window x:Class="DinosaurIsland.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Dinosaur Island" Height="600" Width="600" WindowState="Normal"  Icon="/DinosaurIsland;component/Icon1.ico" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" WindowStartupLocation="CenterOwner">
<Window.Resources>
    <ResourceDictionary>
        <DataTemplate DataType="{x:Type BitmapImage}">
            <Image Source="{Binding}" />
        </DataTemplate>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Resources.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Window.Resources>

<DockPanel>
    <Menu x:Name="MainMenu" DockPanel.Dock="Top">
        <MenuItem Header="_File">
            <MenuItem Header="_Open Dinosaur Island 'snapshot' file..." x:Name="OpenSnapshotFile" Click="OpenSnapshotFile_click" />
            <MenuItem Header="_Save"/>
            <MenuItem Header="_Exit" x:Name="ExitApp" Click="ExitAppClick" />
        </MenuItem>
        <MenuItem Header="_Height Map">
            <MenuItem Header="Load Height Map..." Name="LoadHeightMap" Click="LoadHeightMapClick" />
            <Separator />
            <MenuItem Header="Display Height Map" x:Name="DisplayHeightMap" Click="DisplayHeightMapClick" />
        </MenuItem>
        <MenuItem Header="Terrain">
            <MenuItem Header="Load Terrain Map..." x:Name="LoadTerrainMap" Click="LoadTerrainMap_Click" />
            <MenuItem Header="Draw Terrain..." x:Name="DrawTerrain" Click="DisplayTerrainPaintBoxClick" />
            <MenuItem Header="Save Terrain Map..." x:Name="SaveTerrainMap" Click="SaveTerrainMap_Click"/>
            <MenuItem Header="Get Terrain Data From BMP..." x:Name="TerrainFromBMP" Click="TerrainFromBMP_Click" />
            <Separator />
            <MenuItem Header="Adjust Terrain Transparency..." x:Name="AdjustTerrainTransparency"  Click="AdjustTerrainTransparency_Click"/>
            <MenuItem Header="Display Terrain Map" x:Name="DisplayTerrainMap"  Click="DisplayTerrainMap_Click"/>
        </MenuItem>

        <MenuItem Header="_Vegetation">
            <MenuItem Header="Plant Vegetation..." x:Name="PlantVegetation" Click="PlantVegetation_Click" />
            <Separator />
            <MenuItem Header="Load Vegetation Map..." x:Name="LoadVegetation" Click="LoadVegetation_Click" />
            <MenuItem Header="Save Vegetation Map..." x:Name="SaveVegetation" Click="SaveVegetation_Click" />
            <Separator />
            <MenuItem Header="Display Vegetation"  Click="DisplayVegetation_Click" />

        </MenuItem>

        <MenuItem Header="Dinosaurs">
            <MenuItem Header="Edit / Place Dinosaurs..." x:Name="EditDinosaurs" Click="EditDinosaurs_Click" />
            <Separator />
            <MenuItem Header="Load Dinosaur Map" Name="LoadDinosaurnMap" Click="LoadDinosaurs_Click"/>
            <MenuItem Header="Save Dinosaur Map" Name="SaveDinosaurMap" Click="SaveDinosaurs_Click"/>
        </MenuItem>

        <MenuItem Header="Time">
            <MenuItem Header="Start..." x:Name="AdvanceTime" Click="StartTime_Click" />
            <MenuItem Header="Stop..." x:Name="StopTime" Click="StopTime_Click" />
            <Separator />
            <MenuItem Header="Adjust Time Step..." x:Name="AdjustTimeStep"  Click="AdjustTimeStep_Click"/>
        </MenuItem>

        <MenuItem Header="Help">
            <MenuItem Header="About Dinosaur Island" Name="AboutDinosaurIsland" Click="AboutDinoIslandClick" />
        </MenuItem>
    </Menu>

    <StatusBar DockPanel.Dock="Bottom">
        <TextBlock Name="StatusBarField1">Location = X,Y</TextBlock>
        <Separator/>
        <TextBlock Name="StatusBarField2">Elevation = X</TextBlock>
        <Separator/>
        <TextBlock Name="StatusBarField3">Terrain = None</TextBlock>
        <Separator/>
        <TextBlock Name="StatusBarField4">Plants = None</TextBlock>
        <Separator/>
        <TextBlock Name="StatusBarField5">Dinosaurs = None</TextBlock>
        <Separator/>
        <TextBlock Name="StatusBarField6">Zoom</TextBlock>
        <Separator/>
        <TextBlock Name="StatusBarField7">Time 0:00</TextBlock>
    </StatusBar>

    <Label DockPanel.Dock="Bottom" Content="Scale = 2000 meters" Height="23" HorizontalAlignment="Center"  Name="HorizScaleDisplayText"  Width="127" />
    <Label DockPanel.Dock="Bottom" Content="└───────────────────────────────┴───────────────────────────────────┘" Height="20" HorizontalAlignment="Center"  Name="HorizScaleDisplayLine"  Width="423" />
    <Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>

        <Slider Grid.Column="0" Orientation="Vertical" HorizontalAlignment="Left" Minimum="1" x:Name="slider"/>
    <ScrollViewer Name="scrollViewer" Grid.Column="1" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Visible" Margin="0,0,0,6">

        <Grid Name="grid" Width="400" Height="400" RenderTransformOrigin="0.5,0.5">
                <Grid.RowDefinitions>
                    <RowDefinition Height="37*" />
                    <RowDefinition Height="363*" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="297*" />
                    <ColumnDefinition Width="103*" />
                </Grid.ColumnDefinitions>
                <Grid.LayoutTransform>
                <TransformGroup>
                    <ScaleTransform x:Name="scaleTransform"/>
                </TransformGroup>
            </Grid.LayoutTransform>
            <Viewbox x:Name="viewBox" Margin="-35,-12,-22,22" Grid.ColumnSpan="2" Grid.RowSpan="2">
                    <ContentPresenter x:Name="contentPresenter" Width="350" Height="350" >
                        <ContentPresenter.Content>
                            <Image x:Name="image" Width="350" Height="350">
                                <Image.Source >
                                    <BitmapImage x:Name="HeightMapImage" UriSource="DinoIslandLogo.bmp" />
                                </Image.Source>
                            </Image>
                        </ContentPresenter.Content>
                    </ContentPresenter>
                </Viewbox>
            </Grid>
        </ScrollViewer>
    </Grid>
</DockPanel>

有关更多详细信息,请参阅从 menuitem 更新 WPF 窗口图像,但在 while 循环中不更新

尝试修改代码以遵循以下建议,我有:

  public MainWindow()
    {
        InitializeComponent();
        this.UpdateImage();
        .....



        private void UpdateImage()
    {
        writeableBitmap = new WriteableBitmap(CleanVegMap);
        image.Source = writeableBitmap;
        DrawDinos2d();
    }
4

1 回答 1

2

这个拼凑在一起的示例将解释响应式 UI voodoo 的基础知识:

我猜你的 MenuItem 做了什么manualLockClick,你的循环做了uiLockClick。你需要做dispatchedClick.

后面的代码:

//some usings...

namespace DispatcherSample
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private int _counter = 0;

        public MainWindow()
        {
            InitializeComponent();
            this.UpdateBox();
        }

        private void manualLockClick(object sender, RoutedEventArgs e)
        {
            _counter++;
            this.UpdateBox();
        }

        // runs on the UI thread will lock all updates until done
        private void uiLockClick(object sender, RoutedEventArgs e)
        {
            for (int i = 0; i < 5; i++)
            {
                _counter++;
                this.UpdateBox();
                Thread.Sleep(1000);
            }
        }

        //runs on a background thread, dispatches to UI thread for updates of controls only
        private void dispatchedClick(object sender, RoutedEventArgs e)
        {
            Task.Factory.StartNew(() =>
                {
                    for (int i = 0; i < 5; i++)
                    {
                        _counter++;
                        this.Dispatcher.Invoke(new Action(() => this.UpdateBox()));
                        Thread.Sleep(1000);
                    }
                });
        }

        private void UpdateBox()
        {
            textBox.Text = _counter.ToString();
        }
    }
}

XAML:

<Window x:Class="DispatcherSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Height="350"
        Width="525">
    <StackPanel Orientation="Vertical">
        <Button Content="Manual locking Increment"
                Click="manualLockClick" />
        <Button Content="UI locking countdown"
                Click="uiLockClick" />
        <Button Content="Dispatched background countdown"
                Click="dispatchedClick" />
        <TextBlock x:Name="textBox" />
    </StackPanel>
</Window>

一旦掌握了这一点,请阅读 async/await 何时可以使用 C#4.5+,因为这可以使响应式 UI 变得有趣。

为了正确地做到这一点,你应该在做后台工作时锁定一些输入(或者更好的是你的逻辑),否则当你发送垃圾邮件时会发生你可以看到的事情dispatchedClick

UI 工作本质上是单线程的,除了在 MainWindow 附带的线程上进行 UI 更新/输入处理之外,您将不希望做任何工作。

于 2013-07-18T21:31:46.590 回答