You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 17:37:34