Avalon Dock窗口停靠切换时控件内容消失问题的解决咨询
解决Avalon Dock停靠/取消停靠时控件内容清空的问题
这个问题本质是AvalonDock在处理窗口停靠状态变化时,会销毁并重新创建对应的视图控件——不管是NLog Viewer的ListBox还是普通TextBox,原来的控件实例被销毁后,新创建的控件自然没有之前的内容。要解决这个问题,核心思路是把数据和视图分离,让数据持久化在ViewModel中,视图只负责绑定展示。
针对NLog Viewer的解决方案
直接依赖NLogViewer控件的内置存储会因为控件重建丢失数据,我们可以自定义NLog Target,把日志输出到ViewModel的可观察集合中,再用ListBox绑定这个集合(如果你的NLogViewer版本支持绑定ItemsSource,也可以直接绑定):
1. 在ViewModel中定义日志集合
using System.Collections.ObjectModel; using NLog; public class YourViewModel : INotifyPropertyChanged // 确保实现INotifyPropertyChanged { public ObservableCollection<string> LogEntries { get; } = new ObservableCollection<string>(); private readonly Logger _logger = LogManager.GetCurrentClassLogger(); public YourViewModel() { // 注册自定义NLog Target SetupNLogTarget(); } private void SetupNLogTarget() { var collectionTarget = new ObservableCollectionTarget { LogCollection = LogEntries, Layout = "${longdate} ${level:uppercase=true} ${message} ${exception:format=ToString}" }; var config = LogManager.Configuration; config.AddTarget("collectionTarget", collectionTarget); config.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, collectionTarget)); LogManager.ReconfigExistingLoggers(); } // 你的异常模拟方法 public void PrintException() { static int counter = 0; counter++; try { if (counter % 2 == 0) throw new OutOfMemoryException(); else throw new ArgumentOutOfRangeException(); } catch (Exception ex) { _logger.Error(ex, "发生异常"); } } // INotifyPropertyChanged实现(可使用MVVM框架如Prism、MvvmLight简化) public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
2. 自定义NLog Target
using NLog; using NLog.Targets; using System.Collections.ObjectModel; using System.Windows; [Target("ObservableCollectionTarget")] public class ObservableCollectionTarget : Target { public ObservableCollection<string> LogCollection { get; set; } protected override void Write(LogEventInfo logEvent) { var logMessage = Layout.Render(logEvent); // 确保在UI线程更新集合 Application.Current.Dispatcher.Invoke(() => { LogCollection?.Add(logMessage); }); } }
3. 修改XAML绑定集合
把原来的nlog:NlogViewer换成绑定LogEntries的ListBox:
<Grid> <StackPanel Margin="0,10"> <Grid Visibility="{Binding outputVisibility, UpdateSourceTrigger=PropertyChanged}"> <!-- 替换成绑定集合的ListBox --> <ListBox ItemsSource="{Binding LogEntries}" MinHeight="300" MinWidth="1000" /> </Grid> <Button Command="{Binding ConvertTextCommand}" Margin="5" Height="35">PrintException</Button> <Button Command="{Binding HideTextBoxCommand}" Margin="5">Hide</Button> </StackPanel> </Grid>
针对普通TextBox的解决方案
同样遵循MVVM数据绑定原则,把TextBox的Text属性绑定到ViewModel的字符串属性:
1. ViewModel中添加属性
private string _textContent; public string TextContent { get => _textContent; set { _textContent = value; OnPropertyChanged(nameof(TextContent)); } }
2. 修改XAML绑定
<TextBox Text="{Binding TextContent, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
这样,当AvalonDock重建TextBox控件时,会自动从ViewModel读取TextContent的最新值,不会丢失内容。
验证效果
完成上述修改后,测试窗口的停靠/取消停靠操作:
- 点击
PrintException生成几条日志 - 取消停靠窗口,再重新停靠
- 查看ListBox中的日志是否依然存在;同理验证TextBox的内容是否保留
内容的提问来源于stack exchange,提问作者Rolandas Ulevicius
相关产品推荐
相关产品推荐

