我遇到的问题是WPF应用程序中的图像没有更新。有人建议我“将该方法放在窗口的调度程序调度程序中。尽可能调度到最接近的元素。并且异步......”但是我找不到任何如何执行此操作的示例。
如何从Window的调度程序中异步调用一个方法?
这是while循环中的代码,直到while循环结束才更新:
writeableBitmap = new WriteableBitmap(CleanVegMap);
image.Source = writeableBitmap;
DrawDinos2d();
这是MainWindow的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>
有关详细信息,请参阅WPF window image updating from menuitem but not when in while loop
尝试修改代码以遵循以下建议我有:
public MainWindow()
{
InitializeComponent();
this.UpdateImage();
.....
private void UpdateImage()
{
writeableBitmap = new WriteableBitmap(CleanVegMap);
image.Source = writeableBitmap;
DrawDinos2d();
}
答案 0 :(得分:2)
这个被黑客攻击的示例将解释响应式UI巫术的基础知识:
我猜您的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>
一旦掌握了这一点,请在使用C#4.5 +时阅读async / await,因为这样可以使响应式UI充满乐趣。
要做到这一点,你应该在做背景工作时锁定一些输入(或者更好的是你背后的逻辑),或者当你发垃圾邮件dispatchedClick
时会发生的事情。
UI工作本质上是单线程的,除了您的MainWindow附带的线程上的UI更新/输入处理之外,您还希望不做任何工作。