【问题标题】:WPF- ListView of ItemsControl with TextBoxes performance issue带有 TextBoxes 性能问题的 ItemsControl 的 WPF- ListView
【发布时间】:2018-09-24 07:28:30
【问题描述】:

我正在努力寻找解决 WPF 性能问题的方法。 我有一个 C# 项目,它从串行端口接收一些“消息”并将它们显示在 GUI 上。此 GUI 允许编辑每个接收到的消息的字节,并且还有一个“保存”按钮,可以将编辑后的消息保存到文件中。

对于 GUI,使用带有 WPF 的 MVVM,我为实现上述要求所做的如下:

  • “MainViewModel”类,其中包含“SerialMessageViewModel”的可观察集合。
  • 每个“SerialMessageViewModel”类都包含串行消息(字符串)的“名称”及其数据字节。

视图使用 ListView 显示“SerialMessageViewModel”的集合,对于每个 SerialMessage,我使用 ItemsControl 来显示 MessageData 字节。因为我希望这些字节是可编辑的,所以我使用 TextBox 作为 ItemsControl 的 ItemTemplate。 这里的想法是让 ItemsControl 像 GUI 一样作为“HexEditor”工作。

除了 ListView 的滚动非常慢之外,一切都运行良好。

这使得应用程序使用起来非常令人沮丧.. 我知道像这样使用带有 TextBoxes 的 ItemsControl 的 ListView 确实会生成很多 TextBoxes,但我找不到另一种制作类似内容的方法。 Visual Studio 分析工具显示滚动延迟是由“布局”时间引起的。

关于如何解决这个问题的任何建议? 非常感谢!

查看 XAML

<Window x:Class="WpfListIssue.MainWindow"
    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:WpfListIssue"
    mc:Ignorable="d"
    Title="MainWindow" Height="800" Width="600">
<Window.Resources>
    <ResourceDictionary>
        <local:ByteToHexStringConverter x:Key="ByteToHexStringConverter" />
    </ResourceDictionary>
</Window.Resources>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <ProgressBar Grid.Row="0" Height="20" IsIndeterminate="True" />

    <ListView Grid.Row="1" Margin="10" ItemsSource="{Binding Messages}">
        <ListView.View>
            <GridView>
                <GridViewColumn Header="Message Name" Width="120" DisplayMemberBinding="{Binding MessageName}" />
                <GridViewColumn Header="Message Data" Width="400" >
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <ItemsControl ItemsSource="{Binding MessageData}">
                                <ItemsControl.ItemTemplate>
                                    <DataTemplate>
                                        <TextBox Text="{Binding Value, Converter={StaticResource ByteToHexStringConverter}}" />
                                    </DataTemplate>
                                </ItemsControl.ItemTemplate>
                                <ItemsControl.ItemsPanel>
                                    <ItemsPanelTemplate>
                                        <VirtualizingStackPanel Orientation="Horizontal" />
                                    </ItemsPanelTemplate>
                                </ItemsControl.ItemsPanel>
                            </ItemsControl>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
            </GridView>
        </ListView.View>
    </ListView>
</Grid>
</Window>

视图模型

public class MainViewModel : ViewModelBase
{
    private ObservableCollection<SerialMessageViewModel> _messages;

    public ObservableCollection<SerialMessageViewModel> Messages
    {
        get { return _messages; }
        set
        {
            if (Equals(value, _messages)) return;
            _messages = value;
            RaisePropertyChanged();
        }
    }

    public MainViewModel()
    {
        GetMessages();
    }

    private void GetMessages()
    {
        // Simulates the reception of messages from serial
        Messages = new ObservableCollection<SerialMessageViewModel>();
        Random rand = new Random();

        for (int i = 0; i < 100; i++)
        {
            byte[] data = new byte[50];
            rand.NextBytes(data);
            SerialMessageViewModel message = new SerialMessageViewModel("Message n." + i, data);
            Messages.Add(message);
        }
    }
}

public class SerialMessageViewModel : ViewModelBase
{
    private string _messageName;
    private ObservableCollection<NotifyByte> _messageData;

    public string MessageName
    {
        get { return _messageName; }
        set
        {
            if (Equals(value, _messageData)) return;
            _messageName = value;
            RaisePropertyChanged();
        }
    }

    public ObservableCollection<NotifyByte> MessageData
    {
        get { return _messageData; }
        set
        {
            if (Equals(value, _messageData)) return;
            _messageData = value;
            RaisePropertyChanged();
        }
    }

    public SerialMessageViewModel(string messageName, byte[] data)
    {
        MessageName = messageName;
        MessageData = new ObservableCollection<NotifyByte>();
        foreach (byte b in data)
            MessageData.Add(new NotifyByte(b));
    }
}

public class NotifyByte : ViewModelBase
{
    private byte _value;

    public byte Value
    {
        get { return _value; }
        set
        {
            if (Equals(value, _value)) return;
            _value = value;
            RaisePropertyChanged();
        }
    }

    public NotifyByte(byte value)
    {
        _value = value;
    }
}

ByteToHexStringConverter

public class ByteToHexStringConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        byte num = (byte)value;
        return num.ToString("X2");
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        string input = (string)value;
        if (!String.IsNullOrEmpty(input))
        {
            input = input.Replace(" ", "");
            return System.Convert.ToByte(input, 16);
        }
        return 0x00;
    }
}

【问题讨论】:

  • 了解虚拟化。您可以对显示进行分页,以便用户一次只能看到(比如说)10 个项目。
  • 嗨,@PrateekShrivastava。您是在谈论 ListView 或每个 ItemsControl 上的虚拟化吗?一次 10 个列表视图行太少了
  • 尝试使用分页,每个页面有 10 个页面 - 请参阅滚动性能/行为。然后逐渐增加页面大小,为您的案例找到最佳的每页项目值。闪烁是因为 Screen/Container 上有太多可视项目。
  • @titanicsnake 为什么不用数据网格而不是列表视图?这样,您可以在滚动时显示为静态字符串,然后在单击时切换到编辑器,我想这会提高性能。
  • @titanicsnake 看看我的回答。它不会为您编写代码,但会显示一个示例,说明您如何做到这一点。

标签: wpf performance listview mvvm itemscontrol


【解决方案1】:

这不使用您的示例代码,但总的来说,您可以使用 DataGrid 来实现您正在寻找的内容,但要尝试使其更轻量级。请注意,我使用的是 TextBlock,因为我们不想允许从单元格进行编辑。您的自定义文本框 XAML 将进入 CellEditTemplate。

MainWindow.xaml

<DataGrid AutoGenerateColumns="False"
               ItemsSource="{Binding DataSet}">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding DisplayName}" />
        <DataGridTemplateColumn>
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding DisplayForIsSomethingRelevant}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
            <DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <CheckBox IsChecked="{Binding IsSomethingRelevant}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellEditingTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

MainWindowVm.cs

public class MainWindowVm
{
   public ObservableCollection<TestObject> DataSet { get; set; } = new ObservableCollection<TestObject>();

   public MainWindowVm()
   {
      DataSet.Add(new TestObject {DisplayName = "Booyah", IsSomethingRelevant = true});
      DataSet.Add(new TestObject {DisplayName = "Iggy Boop", IsSomethingRelevant = false});
   }
}

TestObject.cs(bindablebase 只是为 notifypropertychanged 提供了默认实现)

public class TestObject : BindableBase
{
   private string _displayName;

   public string DisplayName
   {
      get { return _displayName; }
      set { SetProperty(ref _displayName, value); }
   }

   private bool _IsSomethingRelevant;

   public bool IsSomethingRelevant
   {
      get { return _IsSomethingRelevant; }
      set
      {
         SetProperty(ref _IsSomethingRelevant, value); 
         NotifyPropertyChanged(nameof(DisplayForIsSomethingRelevant));
      }
   }

   public string DisplayForIsSomethingRelevant => IsSomethingRelevant
                                                     ? "Totes Relevant"
                                                     : "Non-Relevono";
}

【讨论】:

  • 非常感谢!这个解决方案就是我想要的! :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-02
  • 2020-08-27
  • 2016-06-02
相关资源
最近更新 更多