这是indexloc提供的服务,不要输入任何密码
Skip to content

Bug DockingManager not updating state when closing a tab #204

@PatrickHofman

Description

@PatrickHofman

In my app, we update the buttons in a ribbon control based upon the state of the last actived document. This works perfectly, until you remove a document. The newly selected document is activated on the UI side, but it is not activated programmatically, causing the bindings to be set according the new situation.

Test code:

WPF MainWindow:

<Window x:Class="WpfApp2.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:WpfApp2"
        xmlns:xcad="https://github.com/Dirkster99/AvalonDock"
        mc:Ignorable="d"
        d:DataContext="{x:Type local:AppViewModel}"
        Title="MainWindow" Height="450" Width="800">
    <DockPanel>
        <Button Content="Click me" DockPanel.Dock="Top" IsEnabled="{Binding ActiveDocument.IsEnabled}" />

        <xcad:DockingManager DocumentsSource="{Binding Documents, Mode=TwoWay}"
                             AnchorablesSource="{Binding Path=ActiveDocument.ToolPanes}"
                             ActiveContent="{Binding ActivePane, Mode=TwoWay}"
        >
            <DockingManager.DocumentHeaderTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto"/>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>
                        <TextBlock x:Name="TitleText" Grid.Column="1" Margin="1,0,1,0" MaxWidth="200"
                                   Text="{Binding Content.Title, FallbackValue=#ERROR#}"
                                   ToolTip="{Binding Content.Title}" TextTrimming="CharacterEllipsis"/>
                    </Grid>
                </DataTemplate>
            </DockingManager.DocumentHeaderTemplate>
            
            <DockingManager.LayoutItemTemplate>
                <DataTemplate>
                    <Label Content="{Binding Content.Text}" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Background="LightBlue" />
                </DataTemplate>
            </DockingManager.LayoutItemTemplate>
        </xcad:DockingManager>
    </DockPanel>
</Window>

Models:

using System.Collections.ObjectModel;
using System.Windows;

namespace WpfApp2
{
    public class AppViewModel : DependencyObject
    {
        public static readonly DependencyProperty DocumentsProperty = DependencyProperty.Register(nameof(Documents), typeof(ObservableCollection<Document>), typeof(AppViewModel));

        public static readonly DependencyProperty ActiveDocumentProperty = DependencyProperty.Register(nameof(ActiveDocument), typeof(Document), typeof(AppViewModel));

        public static readonly DependencyProperty ActivePaneProperty = DependencyProperty.Register(nameof(ActivePane), typeof(ISomeAvalonDockControl), typeof(AppViewModel), new PropertyMetadata(ActivePane_PropertyChanged));

        private static void ActivePane_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is AppViewModel self)
            {
                if (e.NewValue is Document dvm)
                {
                    self.ActiveDocument = dvm;
                }
                else if (e.NewValue == null)
                {
                    self.ActiveDocument = null;
                }
            }
        }

        public ObservableCollection<Document> Documents
        {
            get { return (ObservableCollection<Document>)this.GetValue(DocumentsProperty); }
            set { this.SetValue(DocumentsProperty, value); }
        }

        public Document ActiveDocument
        {
            get { return (Document)this.GetValue(ActiveDocumentProperty); }
            set { this.SetValue(ActiveDocumentProperty, value); }
        }

        public ISomeAvalonDockControl ActivePane
        {
            get { return (ISomeAvalonDockControl)this.GetValue(ActivePaneProperty); }
            set { this.SetValue(ActivePaneProperty, value); }
        }
    }

    public class Document : DependencyObject, ISomeAvalonDockControl
    {
        public static readonly DependencyProperty TitleProperty = DependencyProperty.Register(nameof(Title), typeof(string), typeof(Document));

        public static readonly DependencyProperty TextProperty = DependencyProperty.Register(nameof(Text), typeof(string), typeof(Document));

        public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.Register(nameof(IsEnabled), typeof(bool), typeof(Document));

        public static readonly DependencyProperty ToolPanesProperty = DependencyProperty.Register(nameof(ToolPanes), typeof(ObservableCollection<ToolPane>), typeof(Document));

        public string Title
        {
            get { return (string)this.GetValue(TitleProperty); }
            set { this.SetValue(TitleProperty, value); }
        }

        public string Text
        {
            get { return (string)this.GetValue(TextProperty); }
            set { this.SetValue(TextProperty, value); }
        }

        public bool IsEnabled
        {
            get { return (bool)this.GetValue(IsEnabledProperty); }
            set { this.SetValue(IsEnabledProperty, value); }
        }

        public ObservableCollection<ToolPane> ToolPanes
        {
            get { return (ObservableCollection<ToolPane>)this.GetValue(ToolPanesProperty); }
            set { this.SetValue(ToolPanesProperty, value); }
        }
    }

    public class ToolPane : DependencyObject, ISomeAvalonDockControl
    {
    }

    public interface ISomeAvalonDockControl
    { }
}

I would expect ActivePane to be updated when a tab is closed. It doesn't and remains null after the it was set to null when closing the previous active document.

To see this visually, do the following:

  1. Start the app.
  2. Select the second document (it is the first document with true in its name). It should enable the button on top.
  3. Close the second document .
  4. The UI gets updated: another document is shown, but the button keeps its old state.

I have made a fix already, but you can argue what document should be activated, and if this is the right place to update the state. I will leave that to you.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions