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} \r\n"); }); //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"