一、动态绑定 (Dynamic Binding)
1. 数据绑定基础
csharp
csharp
// Model类
public class DeviceData : INotifyPropertyChanged
{
private string _deviceName;
private double _temperature;
private DateTime _timestamp;
public string DeviceName
{
get => _deviceName;
set
{
_deviceName = value;
OnPropertyChanged();
}
}
public double Temperature
{
get => _temperature;
set
{
_temperature = value;
OnPropertyChanged();
OnPropertyChanged(nameof(TemperatureFormatted));
}
}
public DateTime Timestamp
{
get => _timestamp;
set
{
_timestamp = value;
OnPropertyChanged();
}
}
// 计算属性
public string TemperatureFormatted => $"{_temperature:F1}°C";
// INotifyPropertyChanged 实现
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
2. 动态绑定集合
csharp
ini
// ViewModel
public class DeviceViewModel : INotifyPropertyChanged
{
private ObservableCollection<DeviceData> _devices;
private DeviceData _selectedDevice;
public ObservableCollection<DeviceData> Devices
{
get => _devices;
set
{
_devices = value;
OnPropertyChanged();
}
}
public DeviceData SelectedDevice
{
get => _selectedDevice;
set
{
_selectedDevice = value;
OnPropertyChanged();
}
}
// 动态添加设备
public void AddDevice(string name)
{
var device = new DeviceData
{
DeviceName = name,
Temperature = 25.0,
Timestamp = DateTime.Now
};
Devices.Add(device);
}
// 动态更新数据
public void UpdateDeviceTemperature(string deviceName, double temperature)
{
var device = Devices.FirstOrDefault(d => d.DeviceName == deviceName);
if (device != null)
{
device.Temperature = temperature;
device.Timestamp = DateTime.Now;
}
}
}
3. XAML绑定示例
xml
xml
<!-- MainWindow.xaml -->
<Window x:Class="MonitorApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<!-- DataGrid动态绑定 -->
<DataGrid ItemsSource="{Binding Devices}"
SelectedItem="{Binding SelectedDevice}"
AutoGenerateColumns="False"
VirtualizingPanel.IsVirtualizing="True">
<DataGrid.Columns>
<DataGridTextColumn Header="设备名称"
Binding="{Binding DeviceName}"/>
<DataGridTextColumn Header="温度"
Binding="{Binding TemperatureFormatted}"/>
<DataGridTextColumn Header="更新时间"
Binding="{Binding Timestamp, StringFormat=HH:mm:ss}"/>
</DataGrid.Columns>
</DataGrid>
<!-- 实时数据显示 -->
<ItemsControl ItemsSource="{Binding Devices}"
VirtualizingPanel.IsVirtualizing="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Margin="5" Padding="10" Background="LightGray">
<StackPanel>
<TextBlock Text="{Binding DeviceName}"
FontWeight="Bold"/>
<TextBlock Text="{Binding TemperatureFormatted}"
Foreground="Red"/>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
</Window>
二、虚拟化 (Virtualization)
1. UI虚拟化实现
csharp
csharp
// 自定义虚拟化数据源
public class VirtualizedDataSource<T> : IList, INotifyCollectionChanged
{
private readonly List<T> _data;
private readonly int _pageSize;
private Dictionary<int, T> _cache;
public VirtualizedDataSource(IEnumerable<T> data, int pageSize = 100)
{
_data = data.ToList();
_pageSize = pageSize;
_cache = new Dictionary<int, T>();
}
// 虚拟化访问
public object this[int index]
{
get
{
if (!_cache.ContainsKey(index))
{
// 加载数据页
LoadPage(index / _pageSize);
}
return _cache[index];
}
set => throw new NotImplementedException();
}
private void LoadPage(int pageNumber)
{
int start = pageNumber * _pageSize;
int end = Math.Min(start + _pageSize, _data.Count);
for (int i = start; i < end; i++)
{
_cache[i] = _data[i];
}
}
public int Count => _data.Count;
// 实现其他必要接口...
}
// 使用示例
public class LargeDataViewModel
{
public VirtualizedDataSource<DeviceData> VirtualizedDevices { get; }
public LargeDataViewModel()
{
// 生成大量数据
var data = Enumerable.Range(1, 100000)
.Select(i => new DeviceData
{
DeviceName = $"设备_{i}",
Temperature = 20 + i % 30,
Timestamp = DateTime.Now.AddSeconds(-i)
});
VirtualizedDevices = new VirtualizedDataSource<DeviceData>(data);
}
}
2. 高级虚拟化配置
xml
xml
<!-- 虚拟化ListView -->
<ListView ItemsSource="{Binding VirtualizedDevices}"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling"
VirtualizingPanel.ScrollUnit="Pixel"
VirtualizingPanel.CacheLength="100,100"
ScrollViewer.CanContentScroll="True"
ScrollViewer.IsDeferredScrollingEnabled="True">
<ListView.View>
<GridView>
<GridViewColumn Header="ID"
DisplayMemberBinding="{Binding DeviceName}"/>
<GridViewColumn Header="温度"
DisplayMemberBinding="{Binding TemperatureFormatted}"/>
</GridView>
</ListView.View>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel VirtualizingPanel.ScrollUnit="Pixel"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
<!-- 虚拟化TreeView -->
<TreeView VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
3. 异步虚拟化加载
csharp
csharp
public class AsyncVirtualizedCollection<T> : ObservableCollection<T>, IList, INotifyCollectionChanged
{
private readonly Func<int, Task<IEnumerable<T>>> _dataLoader;
private readonly int _pageSize;
private readonly Dictionary<int, List<T>> _loadedPages;
private int _totalCount;
public AsyncVirtualizedCollection(
Func<int, int, Task<IEnumerable<T>>> dataLoader,
int totalCount,
int pageSize = 50)
{
_dataLoader = (page) => dataLoader(page * pageSize, pageSize);
_totalCount = totalCount;
_pageSize = pageSize;
_loadedPages = new Dictionary<int, List<T>>();
}
// 重写索引器实现虚拟化
public new T this[int index]
{
get
{
int page = index / _pageSize;
int pageIndex = index % _pageSize;
if (!_loadedPages.ContainsKey(page))
{
// 异步加载页面
LoadPageAsync(page).Wait();
}
return _loadedPages[page][pageIndex];
}
set => base[index] = value;
}
private async Task LoadPageAsync(int page)
{
var data = await _dataLoader(page);
_loadedPages[page] = data.ToList();
// 触发UI更新
OnCollectionChanged(new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Reset));
}
// 实现其他必要方法...
}
// 使用示例
public class DataService
{
public async Task<IEnumerable<DeviceData>> GetDevicesAsync(int skip, int take)
{
// 模拟数据库查询
await Task.Delay(100);
return Enumerable.Range(skip, take)
.Select(i => new DeviceData
{
DeviceName = $"Device_{i}",
Temperature = 20 + i % 30
});
}
}
三、性能优化技巧
1. 绑定优化
csharp
csharp
// 1. 使用延迟绑定
public class DelayedBindingHelper
{
public static readonly DependencyProperty DelayedTextProperty =
DependencyProperty.RegisterAttached(
"DelayedText",
typeof(string),
typeof(DelayedBindingHelper),
new FrameworkPropertyMetadata(
null,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
OnDelayedTextChanged,
null,
false,
UpdateSourceTrigger.LostFocus));
// 2. 绑定验证和转换优化
public class TemperatureConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double temp)
{
return $"{temp:F2} °C";
}
return string.Empty;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string str && double.TryParse(str, out double temp))
{
return temp;
}
return 0.0;
}
}
}
2. 内存管理
csharp
csharp
public class MemoryOptimizedListBox : ListBox
{
// 实现自定义虚拟化面板
protected override void PrepareContainerForItemOverride(
DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
// 为可见项设置数据上下文
if (element is FrameworkElement frameworkElement)
{
frameworkElement.DataContext = item;
}
}
protected override void ClearContainerForItemOverride(
DependencyObject element, object item)
{
// 清理资源
if (element is FrameworkElement frameworkElement)
{
frameworkElement.DataContext = null;
}
base.ClearContainerForItemOverride(element, item);
}
}
四、实际应用示例
监控系统示例
csharp
csharp
public class MonitoringSystem
{
private readonly Timer _updateTimer;
private readonly Random _random;
private readonly DeviceViewModel _viewModel;
public MonitoringSystem(DeviceViewModel viewModel)
{
_viewModel = viewModel;
_random = new Random();
_updateTimer = new Timer(UpdateData, null, 0, 1000);
}
private void UpdateData(object state)
{
// 模拟实时数据更新
foreach (var device in _viewModel.Devices)
{
device.Temperature = 20 + _random.NextDouble() * 15;
device.Timestamp = DateTime.Now;
}
}
// 动态添加/移除设备
public void AddNewDevice()
{
Application.Current.Dispatcher.Invoke(() =>
{
_viewModel.AddDevice($"设备_{Guid.NewGuid().ToString().Substring(0, 8)}");
});
}
}
配置虚拟化参数
xml
xml
<!-- App.xaml 或 资源字典 -->
<Application.Resources>
<Style TargetType="ListBox">
<Setter Property="VirtualizingPanel.IsVirtualizing" Value="True"/>
<Setter Property="VirtualizingPanel.VirtualizationMode" Value="Recycling"/>
<Setter Property="VirtualizingPanel.CacheLength" Value="50,50"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="True"/>
</Style>
<Style TargetType="DataGrid">
<Setter Property="EnableRowVirtualization" Value="True"/>
<Setter Property="EnableColumnVirtualization" Value="True"/>
<Setter Property="RowHeight" Value="25"/>
<Setter Property="VirtualizingPanel.ScrollUnit" Value="Pixel"/>
</Style>
</Application.Resources>
五、最佳实践总结
-
动态绑定:
- 使用
ObservableCollection<T>实现动态集合 - 实现
INotifyPropertyChanged实现属性变更通知 - 使用
Dispatcher在UI线程更新
- 使用
-
虚拟化优化:
- 大量数据时启用虚拟化
- 使用回收模式 (
VirtualizationMode="Recycling") - 合理设置缓存长度
- 避免嵌套虚拟化容器
-
性能监控:
csharp
ini// 监控虚拟化性能 PresentationTraceSources.DataBindingSource.Switch.Level = SourceLevels.Warning | SourceLevels.Error; -
内存优化:
- 及时释放不需要的绑定
- 使用弱引用处理大型对象
- 分页加载大数据集
这些技术结合使用,可以显著提升C#上位机应用程序的性能和响应速度,特别是在处理大量实时数据时。