WPF如何在GUI自由时显示Gif动画
本文关键字:显示 Gif 动画 自由 GUI WPF | 更新日期: 2023-09-27 18:00:23
代码示例:
XAML
<Window x:Class="TestWpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:testWpfApp="clr-namespace:TestWpfApp"
Title="MainWindow" Height="350" Width="525">
<Grid>
<testWpfApp:GifImage x:Name="gifImage" Stretch="None" GifSource="/loading.gif" AutoStart="True" Width="50" Height="50" Margin="0,0,300,0"/>
<ComboBox Width="200" Height="40" Name="Cb"></ComboBox>
</Grid>
</Window>
其中GifImage
是来自《我如何获得动画gif以在WPF中工作》的高分答案的类?
后面的C#代码
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
for (int i = 0; i < 14000; i++)
{
Cb.Items.Add("Item number" + i);
}
}
}
当我第一次点击组合框时,我会在5-7秒内冻结GUI,并且我想在GUI冻结时显示动画gif。我该怎么做?
您想要这样的东西:
第一个VisualWrapper
类:
[ContentProperty("Child")]
public class VisualWrapper : FrameworkElement
{
public Visual Child
{
get
{
return _child;
}
set
{
if (_child != null)
{
RemoveVisualChild(_child);
}
_child = value;
if (_child != null)
{
AddVisualChild(_child);
}
}
}
protected override Visual GetVisualChild(int index)
{
if (_child != null && index == 0)
{
return _child;
}
else
{
throw new ArgumentOutOfRangeException("index");
}
}
protected override int VisualChildrenCount
{
get
{
return _child != null ? 1 : 0;
}
}
private Visual _child;
}
然后VisualTargetPresentationSource
:
public class VisualTargetPresentationSource : PresentationSource
{
public VisualTargetPresentationSource(HostVisual hostVisual)
{
_visualTarget = new VisualTarget(hostVisual);
}
public override Visual RootVisual
{
get
{
return _visualTarget.RootVisual;
}
set
{
Visual oldRoot = _visualTarget.RootVisual;
// Set the root visual of the VisualTarget. This visual will
// now be used to visually compose the scene.
_visualTarget.RootVisual = value;
// Tell the PresentationSource that the root visual has
// changed. This kicks off a bunch of stuff like the
// Loaded event.
RootChanged(oldRoot, value);
// Kickoff layout...
UIElement rootElement = value as UIElement;
if (rootElement != null)
{
rootElement.Measure(new Size(Double.PositiveInfinity,
Double.PositiveInfinity));
rootElement.Arrange(new Rect(rootElement.DesiredSize));
}
}
}
protected override CompositionTarget GetCompositionTargetCore()
{
return _visualTarget;
}
public override bool IsDisposed
{
get
{
// We don't support disposing this object.
return false;
}
}
private VisualTarget _visualTarget;
}
这在你的XAML中:
<Window x:Class="TestWpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:testWpfApp="clr-namespace:TestWpfApp"
Title="MainWindow" Height="350" Width="525"
xmlns:vw="--NamespaceHere--">
<Grid>
<testWpfApp:GifImage x:Name="gifImage" Stretch="None" GifSource="/loading.gif" AutoStart="True" Width="50" Height="50" Margin="0,0,300,0"/>
<ComboBox Width="200" Height="40" Name="Cb"></ComboBox>
<vw:VisualWrapper Height="160" Width="160" VerticalAlignment="Center" HorizontalAlignment="Center" x:Name="visualWrapper" />
</Grid>
</Window>
然后LoadingPanel
类:
public class LoadingPanel
{
private VisualWrapper.VisualWrapper visualWrapper;
public LoadingPanel(VisualWrapper.VisualWrapper _visualWrapper)
{
visualWrapper = _visualWrapper;
}
public VisualWrapper.VisualWrapper VisualWrapper
{
get { return visualWrapper; }
set { visualWrapper = value; }
}
#region WaitDailog
public HostVisual CreateMediaElementOnWorkerThread()
{
// Create the HostVisual that will "contain" the VisualTarget
// on the worker thread.
HostVisual hostVisual = new HostVisual();
// Spin up a worker thread, and pass it the HostVisual that it
// should be part of.
Thread thread = new Thread(new ParameterizedThreadStart(MediaWorkerThread));
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start(new object[] { hostVisual, visualWrapper });
// Wait for the worker thread to spin up and create the VisualTarget.
s_event.WaitOne();
return hostVisual;
}
private FrameworkElement CreateMediaElement(VisualWrapper visualWrapper)
{
BitmapImage bi = new BitmapImage(new Uri(--YOURIMAGEPATH--));//Image path goes here
Image image = new Image();
image.Source = bi;
image.Height = 150;
image.Width = 150;
//image.Margin = new Thickness(-150, -150, -150, -150);
ImageBehavior.SetAnimatedSource(image, bi);//See http://wpfanimatedgif.codeplex.com/
BrushConverter conv = new BrushConverter();
//SolidColorBrush brush = conv.ConvertFromString("#6C8BBA") as SolidColorBrush;
Border border = new Border();
border.Background = Brushes.Transparent;
//border.BorderBrush = brush;
//border.BorderThickness = new Thickness(3);
//border.Margin = new Thickness(-85, -140, 0, 0);
border.Child = image;
return border;
}
private void MediaWorkerThread(object arg)
{
// Create the VisualTargetPresentationSource and then signal the
// calling thread, so that it can continue without waiting for us.
HostVisual hostVisual = (HostVisual)((object[])arg)[0];
VisualWrapper visualWrapper = (VisualWrapper)((object[])arg)[1];
VisualTargetPresentationSource visualTargetPS = new VisualTargetPresentationSource(hostVisual);
s_event.Set();
// Create a MediaElement and use it as the root visual for the
// VisualTarget.
visualTargetPS.RootVisual = CreateMediaElement(visualWrapper);
// Run a dispatcher for this worker thread. This is the central
// processing loop for WPF.
System.Windows.Threading.Dispatcher.Run();
}
private static AutoResetEvent s_event = new AutoResetEvent(false);
public bool ShowWaitDialog()
{
if (visualWrapper != null)
{
if (visualWrapper.Child == null)
{
visualWrapper.Child = CreateMediaElementOnWorkerThread();
}
}
return true;
}
public bool DisposeWaitDialog()
{
if (visualWrapper != null)
{
visualWrapper.Child = null;
}
return true;
}
#endregion
}
代码:
public MainWindow()
{
InitializeComponent();
VisualWrapper visualWrapper = (VisualWrapper)this.FindName("visualWrapper");
LoadingPanel loadingPanel = new LoadingPanel(visualWrapper);
Dispatcher.Invoke((Action)(() =>
{
loadingPanel.ShowWaitDialog();
}), DispatcherPriority.Send, null);
Task.Factory.StartNew(() =>
{
List<string> list = new List<string>();
for (int i = 0; i < 14000; i++)
{
list.Add("Item number" + i);
}
Dispatcher.BeginInvoke((Action)(() =>
{
Cb.ItemsSource = list;
}), DispatcherPriority.Normal, null);
}, TaskCreationOptions.LongRunning);
}
编辑VirtualizingStackPanel
:
An easy way to implement this is to create an ItemsPanelTemplate as a Resource and reference it in the ComboBox markup.
<Window.Resources>
<ItemsPanelTemplate x:Key="VSP">
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</Window.Resources>
<ComboBox Height="23" Margin="27,10,10,0" Name="ComboBox1"
VerticalAlignment="Top"
ItemsSource="{Binding}"
ItemsPanel="{StaticResource VSP}"
ScrollViewer.IsDeferredScrollingEnabled="True">
</ComboBox>
Specifically, the ItemsPanel property of the ComboBox is set to that ItemsPanelTemplate Resource.
If you prefer, you can include the VirtualizingStackPanel right in the ComboBox creation markup:
<ComboBox Height="23" Margin="27,10,10,0" Name="ComboBox1"
VerticalAlignment="Top"
ItemsSource="{Binding}"
ScrollViewer.IsDeferredScrollingEnabled="True">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
</ComboBox>
您的问题是创建了一个包含14000个项目的组合框。这将需要很长时间才能创建,并且在创建后将无法使用。你真的希望你的用户从14000个列表中选择一个项目吗?
重新思考您的UI。如果你真的需要让用户从14000种可能性的列表中进行选择,可以考虑一个带有typeahead的搜索文本字段。
如果您绝对必须拥有一个包含数千项的ComboBox,请研究控件虚拟化。