1

Code sample:

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>

where GifImage is class from high rated answer from How do I get an animated gif to work in WPF?

C# code-behind

public partial class MainWindow : Window
{


    public MainWindow()
    {
        InitializeComponent();
        for (int i = 0; i < 14000; i++)
        {
            Cb.Items.Add("Item number" + i);
        }


    }
}

When I make first click on combobox I get GUI freeze on 5-7 seconds and I want to show animated gif when GUI is freeze. How can I make it?

4

2 回答 2

4

你想要这样的东西:

一级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> 
于 2013-02-21T19:34:33.490 回答
-2

您的问题是您正在创建一个包含 14000 个项目的 ComboBox。这将需要很长时间才能创建,并且在创建后将无法使用。您真的希望您的用户从 14000 个列表中选择一个项目吗?

重新考虑你的用户界面。如果您确实需要让用户从 14000 种可能性的列表中进行选择,请考虑使用预先输入的搜索文本字段。

如果您绝对必须拥有一个包含数千个项目的 ComboBox,请查看控制虚拟化

于 2013-02-21T19:15:49.543 回答