WPF界面数据延迟显示问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WPF界面数据延迟显示问题相关的知识,希望对你有一定的参考价值。

现在做的软件界面上要显示1000行的数据,每行数据有8个控件,通过数据绑定显示数据时界面卡死,大概要反应10秒左右后才能显示数据,想请教大神们有什么方法可以减少延时,快速显示?

参考技术A 1000行的数据一次显示是不科学的,最好的方式是分页显示,每页20行左右,简单可行。

第二种方式是缓存的形式,类似于微博那样,先显示前50行,当下拉到45行时,加载51—100行的内容。

最后一种方式是多线程方式,第一条线程加载页面控件,第二条线程加载前50行内容(保证用户体验),第三条线程加载余下的内容本回答被提问者和网友采纳
参考技术B 异步加载数据吧,数据源用ObservableCollection

另外,从你的截图来看,可以用DataGrid,因为DataGrid有Virtulization,构造UI只构造可见部分,节省。话说即使异步加载,1000行数据生成完了也会卡的够呛
参考技术C 有几种做法。做法一:做个分页,datagrid只加载当前页的数据;做法二,多线程操作。UI显示和数据加载分不同的线程完成。

wpf 滚屏数据显示

近期库房想在出库存放区划分货位存放不同客户拣货后的商品数据。
同时需要在货位摆放屏幕以便显示当前货位被那个客户拣货占用,及商品信息、拣货状态等

 

由于独立项目,数据来源于api接口,所以只是一个客户端轮播即可。故拿wpf来试试demo

设计为:

1、一个主界面为控制台控制第2,3,....屏显示不同客户(货位)的信息去请求不同的数据

2、第2、3...屏的轮播数据展示

 

实现:

1、创建项目net下都一致,略过

2、控制不同屏幕轮播不同信息,只需要相关配置即可,这里略过

3、显示轮播信息这里使用一个按钮跳转来完成

<Button  Grid.Column="2" Content="滚动" Name="btn_scroll" Width="100" Click="btn_Scroll_Click"/>

跳转到次屏

private void btn_Scroll_Click(object sender, RoutedEventArgs e)
        {
            ScrollWindow w = new ScrollWindow();
            MultipScreenManager.ShowInScreen(w);
        }

 

 

这里使用到一个主次屏管理,引用了某一位博主(当前忘记了是哪一位,有知道的通知我补登)的MultipScreenManager

    public static class MultipScreenManager
    {
        #region Property

        internal static Screen[] AllScreens
        {
            get
            {
                return Screen.AllScreens;
            }
        }

        internal static Screen PrimaryScreen
        {
            get
            {
                return Screen.PrimaryScreen;
            }
        }

        internal static IEnumerable<Screen> MinorScreens
        {
            get
            {
                return Screen.AllScreens.Where(o => o.Primary == false);

            }
        }

        internal static Screen FirstMinorScreen
        {
            get
            {
                return MinorScreens.FirstOrDefault();
            }
        }

        #endregion Property

        #region Method
        public static void ShowInScreen(this System.Windows.Window win)
        {
            SetScreen(win);
            win.Show();
        }
        public static void ShowDialogInScreen(this System.Windows.Window win)
        {
            SetScreen(win);
            win.ShowDialog();
        }

        private static void SetScreen(System.Windows.Window win)
        {
            var attr = win.GetType().GetCustomAttributes(typeof(MultipScreenAttribute), false).FirstOrDefault(o => o is MultipScreenAttribute);
            int index = 0;
            bool ingoreOperation = false;
            WindowStartupLocationInScreen inScreen = WindowStartupLocationInScreen.CenterScreen;
            if (attr != null)
            {
                var temp = (attr as MultipScreenAttribute);
                index = temp.Index;
                inScreen = temp.InScreen;
                ingoreOperation = temp.IngoreMinorScreenError;
            }
            Screen screen = PrimaryScreen;
            if (index == 1 && FirstMinorScreen != null)
            {
                screen = FirstMinorScreen;
            }
            else if (index > 1 && index < MinorScreens.Count())
            {
                screen = MinorScreens.ElementAt(index);
            }
            else if (index > 0 && index >= MinorScreens.Count() && ingoreOperation)
            {
                return;
            }

            switch (inScreen)
            {
                case WindowStartupLocationInScreen.CenterScreen:
                    SetWindowInScreenCenter(win, screen);
                    break;
                case WindowStartupLocationInScreen.Manual:
                    SetWindowInScreenManual(win, screen);
                    break;
            }
        }

        private static void SetWindowInScreenCenter(System.Windows.Window win, Screen screen)
        {
            win.Top = screen.WorkingArea.Y + (screen.WorkingArea.Height - win.Height) / 2;
            win.Left = screen.WorkingArea.X + (screen.WorkingArea.Width - win.Width) / 2;
        }
        private static void SetWindowInScreenManual(System.Windows.Window win, Screen screen)
        {
            win.Top = screen.WorkingArea.Y;
            win.Left = screen.WorkingArea.X;
        }

        #endregion Method
    }

    [AttributeUsage(AttributeTargets.Class)]
    public class MultipScreenAttribute:Attribute
    {
        public MultipScreenAttribute(ScreenType type = ScreenType.Primary, WindowStartupLocationInScreen inScreen = WindowStartupLocationInScreen.CenterScreen)
            : this((int)type, inScreen)
        {
        }
        public MultipScreenAttribute(int index = 0, WindowStartupLocationInScreen inScreen = WindowStartupLocationInScreen.CenterScreen)
        {
            Index = index;
            InScreen = inScreen;
        }
        /// <summary>
        /// 在窗体初始化显示的位置
        /// </summary>
        public WindowStartupLocationInScreen InScreen { get; private set; }
        /// <summary>
        /// 屏幕索引, 0为主屏,1+为次屏
        /// </summary>
        public int Index { get; private set; }
        /// <summary>
        /// 当任何指定次屏没有找到时,如果该值为TRUE,则忽略这个页面的显示,否则将显示在主屏
        /// </summary>
        public bool IngoreMinorScreenError { get; private set; }
    }

    public enum ScreenType
    {
        /// <summary>
        /// 主屏
        /// </summary>
        Primary = 0,
        /// <summary>
        /// 次屏
        /// </summary>
        Minor = 1,
    }

    public enum WindowStartupLocationInScreen
    {
        Manual = 0,
        CenterScreen = 1,
    }

 

次屏ui:

<Window x:Class="WpfApp1.ScrollWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="ScrollWindow"
        Loaded="ScrollWindow_Loaded" >
    <!--MouseEnter="ScrollWindow_MouseEnter" MouseLeave="ScrollWindow_MouseLeave" MouseMove="ScrollWindow_MouseMove"-->

    <Window.Resources>
        <Storyboard x:Key="storyboard">
            <DoubleAnimation  Duration="0:0:1" From="300" To="0" Storyboard.TargetName="stackPanel" Storyboard.TargetProperty="RenderTransform.Y"/>
        </Storyboard>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="100"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid Grid.Row="0">
            <Grid.RowDefinitions>
                <RowDefinition Height="50"></RowDefinition>
                <RowDefinition Height="50"></RowDefinition>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="170"></ColumnDefinition>
                <ColumnDefinition Width="*"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Label Grid.RowSpan="2" Grid.Column="0" x:Name="txtHHName" Content="张三" FontWeight="Bold" FontSize="50" VerticalAlignment="Center" HorizontalAlignment="Center" />
            <Label Grid.Row="0" Grid.Column="1" x:Name="txtContract"  Content="1003175664863" FontSize="30"></Label>
            <Label Grid.Row="1" Grid.Column="1" x:Name="txtAddress"  Content="北京市大兴区旧宫镇 住总万科广场C座11层" FontSize="30"></Label>
        </Grid>
        <ScrollViewer Grid.Row="1" Name="scrollViewer" HorizontalScrollBarVisibility="Hidden"
                      HorizontalContentAlignment="Stretch"
                      VerticalScrollBarVisibility="Hidden"
                       VerticalContentAlignment="Stretch">
            <Border>
                <StackPanel x:Name="stackPanel" Margin="5 5 5 5" >
                    <StackPanel.RenderTransform>
                        <TranslateTransform />
                    </StackPanel.RenderTransform>
                    <!--<Label x:Name="lab_text" FontSize="20"></Label>-->

                    <DataGrid x:Name="orderItem_list" ItemsSource="{Binding Results,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}" 
                              IsReadOnly="True" AutoGenerateColumns="False" GridLinesVisibility="None"
                              HorizontalAlignment="Stretch" VerticalAlignment="Stretch" HorizontalContentAlignment="Stretch"
                              BorderThickness="0" FontSize="25" EnableRowVirtualization="false" EnableColumnVirtualization="False" 
                              CanUserAddRows="False" CanUserReorderColumns="False" CanUserResizeColumns="False" AlternationCount="2" HeadersVisibility="None">
                        <DataGrid.RowHeaderStyle>
                            <Style TargetType="DataGridRowHeader">
                                <Setter Property="FontSize" Value="30"></Setter>
                                <Setter Property="FontWeight" Value="Bold"></Setter>
                            </Style>
                        </DataGrid.RowHeaderStyle>
                        <DataGrid.RowStyle>
                            <Style TargetType="{x:Type DataGridRow}">
                                <Style.Triggers>
                                    <Trigger Property="ItemsControl.AlternationIndex" Value="0">
                                        <Setter Property="Background" Value="White"></Setter>
                                    </Trigger>
                                    <Trigger Property="ItemsControl.AlternationIndex" Value="1">
                                        <Setter Property="Background" Value="WhiteSmoke"></Setter>
                                    </Trigger>
                                </Style.Triggers>
                                <Setter Property="Height" Value="40"></Setter>
                            </Style>
                        </DataGrid.RowStyle>

                        <DataGrid.CellStyle>
                            <Style TargetType="{x:Type DataGridCell}">
                                <Style.Triggers>
                                    <DataTrigger Binding="{Binding StatusName}" Value="未拣货">
                                        <Setter Property="Foreground" Value="Red" />
                                    </DataTrigger>
                                    <DataTrigger Binding="{Binding StatusName}" Value="已拣货">
                                        <Setter Property="Foreground" Value="SlateBlue" />
                                    </DataTrigger>
                                    <DataTrigger Binding="{Binding StatusName}" Value="已复核">
                                        <Setter Property="Foreground" Value="LightGreen" />
                                    </DataTrigger>
                                </Style.Triggers>
                                <Setter Property="VerticalAlignment" Value="Center"></Setter>
                                <Setter Property="VerticalContentAlignment" Value="Center"></Setter>
                                <Setter Property="HorizontalAlignment" Value="Center"></Setter>
                                <Setter Property="HorizontalContentAlignment" Value="Center"></Setter>
                            </Style>
                        </DataGrid.CellStyle>

                        <DataGrid.Columns>
                            <DataGridTextColumn Header="Sku" Binding="{Binding SkuNo}" Width="160"></DataGridTextColumn>
                            <DataGridTextColumn Header="名称" Binding="{Binding SkuName}" Width="300"></DataGridTextColumn>
                            <DataGridTextColumn Header="数量" Binding="{Binding Amount}" Width="100"></DataGridTextColumn>
                            <DataGridTextColumn Header="状态" Binding="{Binding StatusName}" Width="120"></DataGridTextColumn>
                        </DataGrid.Columns>
                    </DataGrid>
                </StackPanel>
            </Border>
        </ScrollViewer>
    </Grid>
</Window>

 

次屏逻辑交互:

/// <summary>
    /// ScrollWindow.xaml 的交互逻辑
    /// </summary>
    [MultipScreen(1, WindowStartupLocationInScreen.CenterScreen)]
    public partial class ScrollWindow : Window
    {
        private System.Timers.Timer _timer;
        private ScrollWindowDataContextModel _data;
        private int _index;
        private int PageSize = 21;
        private double Interval = 5000;
        private int pageCount;
        private Storyboard _storyboard;

        private int typeBatch = 1;
        public ScrollWindow()
        {
            AutoScreen();//需要设置高度、宽度才会遵循MultipScreen的指定显示屏显示
            //this.Width = 800;
            //this.Height = 500;
            InitializeComponent();

            this.KeyDown += ScrollWindow_KeyDown;

            _data = GetAllData();
            txtHHName.Content = $"{_data.HHName}";
            txtContract.Content = $"合同号:{_data.ContractCode}";
            txtAddress.Content = $"地址:{_data.Address}";
        }

        private void ScrollWindow_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Escape)//Esc键  
            {
                this.Close();
            }
        }

        private void ScrollWindow_Loaded(object sender, RoutedEventArgs e)
        {
            //AutoScreen();


            if (_timer == null)
            {
                _storyboard = (Storyboard)this.FindResource("storyboard");

                System.Threading.Tasks.Task.Factory.StartNew(() =>
                {
                    pageCount = (_data.Results.Count() + PageSize - 1) / PageSize;
                    _index= 1;
                    Dispatcher.BeginInvoke(new Action(() =>
                    {
                        stackPanel.RenderTransform = new TranslateTransform(0, 0);
                    }));
                    ShowData();
                });

                _timer = new System.Timers.Timer();
                _timer.Interval = Interval;
                _timer.Elapsed += Action;
                _timer.Start();
            }
        }

        private void AutoScreen()
        {
            this.WindowState = System.Windows.WindowState.Normal;
            this.WindowStyle = System.Windows.WindowStyle.None;
            this.ResizeMode = System.Windows.ResizeMode.NoResize;

            //this.Topmost = true;

            if (System.Windows.Forms.Screen.AllScreens.Where(o => o.Primary == false).Count() == 0)
            {
                this.Left = 0.0;
                this.Top = 0.0;
                this.Width = System.Windows.SystemParameters.WorkArea.Width;//.PrimaryScreenWidth;
                this.Height = System.Windows.SystemParameters.WorkArea.Height;//.PrimaryScreenHeight;
            }
            else
            {
                var minorScreen = System.Windows.Forms.Screen.AllScreens.Where(o => o.Primary == false).First();
                this.Left = 0.0;
                this.Top = 0.0;
                var name = minorScreen.DeviceName;
                this.Width = minorScreen.WorkingArea.Width;
                this.Height = minorScreen.WorkingArea.Height;
            }
        }

        private ScrollWindowDataContextModel GetAllData(int type=0)
        {
            ScrollWindowDataContextModel allData = new ScrollWindowDataContextModel();
            allData.HHName = "赵四"; 
            allData.ContractCode = "1003175664863";
            allData.Address = "北京市大兴区旧宫镇 住总万科广场C座11层";
            List<WindowScrollResultModel> rList = new List<WindowScrollResultModel>();
            for (int i = 0; i <= 100; i++)
            {
                WindowScrollResultModel r = new WindowScrollResultModel()
                {
                    SkuNo = $"sku{i.ToString()}-{type}",
                    SkuName = $"sku{i.ToString()}-{type}",
                    Amount = i,
                    StatusName = i.ToString().Contains("3") ? "未拣货" : i % 2 == 0 ? "已拣货" : "已复核",
                };
                rList.Add(r);
            }
            allData.Results = new ObservableCollection<WindowScrollResultModel>(rList);
            return allData;
        }

        private void Action(object sender, ElapsedEventArgs e)
        {
            Dispatcher.BeginInvoke(new Action(() =>
            {
                stackPanel.RenderTransform = new TranslateTransform(0, 0);
                _storyboard.Begin();
            }));

            _index++;
            if (_index > pageCount)
            {
                
                // 可以在这循环完一轮后重新加载数据
                _data = GetAllData(typeBatch);
                typeBatch++;

                _index = 1;
            }

            ShowData();
        }

        private void ShowData()
        {
            Dispatcher.BeginInvoke(new Action(() =>
            {
                List<WindowScrollResultModel> dataList = GetPageData(_index);
                StringBuilder sbMsg = new StringBuilder();
                dataList.ForEach(x=> {
                    sbMsg.Append($"{x.SkuNo}     {x.SkuName}      {x.Amount}      {x.StatusName} 
");
                });
                //lab_text.Content = sbMsg.ToString() ;

                orderItem_list.ItemsSource = dataList;
            }));
        }

        private List<WindowScrollResultModel> GetPageData(int pageIndex)
        {
            if (_data != null)
            {
                return _data.Results.Skip((pageIndex - 1) * PageSize).Take(PageSize).ToList();
            }
            return null;
        }

        private void ScrollWindow_MouseEnter(object sender, MouseEventArgs e)
        {
            _timer.Stop();
        }

        private void ScrollWindow_MouseLeave(object sender, MouseEventArgs e)
        {
            _timer.Start();
        }

        private void ScrollWindow_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                this.DragMove();

                //AutoScreen();
            }
        }
    }

    public class ScrollWindowDataContextModel : NotificationObject
    {
        public string _HHName;
        public string HHName
        {
            get => _HHName; set { _HHName = value; RaisePropertyChanged("HHName"); }
        }

        public string _ContractCode;
        public string ContractCode
        {
            get => _ContractCode; set { _ContractCode = value; RaisePropertyChanged("ContractCode"); }
        }

        public string _Address;
        public string Address
        {
            get => _Address; set { _Address = value; RaisePropertyChanged("Address"); }
        }

        public ObservableCollection<WindowScrollResultModel> results;
        public ObservableCollection<WindowScrollResultModel> Results
        {
            get => results; set { results = value; RaisePropertyChanged("Results"); }
        }
    }

    public class WindowScrollResultModel
    {
        public string SkuNo { get; set; }

        public string SkuName { get; set; }

        public int Amount { get; set; }

        public string StatusName { get; set; }
    }

注意点:
让次屏全屏显示时让width、height=获取到的屏幕大小

开启timer设置间隔时间轮播,默认从第一页开始,设定每屏幕可轮播的行数,到最后一页后重新从第一页轮播

也可以开启鼠标经过轮播体时暂停,离开时继续:MouseEnter="ScrollWindow_MouseEnter" MouseLeave="ScrollWindow_MouseLeave"

Storyboard的DoubleAnimation设置属性控制进入轮播的进入效果及位置等

需要ui好看点,可以引入皮肤库,这这里使用了:MaterialDesign 

只需要在app.xaml中增加配置:

<Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Light.xaml"></ResourceDictionary>
                <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" ></ResourceDictionary>
                <ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Primary/MaterialDesignColor.DeepPurple.xaml"></ResourceDictionary>
                <ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Accent/MaterialDesignColor.Lime.xaml"></ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>

然后在使用页面引入:xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"

 

 

 

MaterialDesign 

以上是关于WPF界面数据延迟显示问题的主要内容,如果未能解决你的问题,请参考以下文章

WPF的问题 一个内容控件,打开界面的时候,我想先让界面显示出来,再加载数据,有啥办法没?

WPF学习之深入浅出话模板

WPF模板详细介绍

WPF 客户端浏览器 添加Loading加载进度

在线等!怎么在wpf中利用后台的代码给定位置,大小和source,创建image,并在界面显示出来。

使用WPF技术模拟手机界面