Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug DockingManager not updating state when closing a tab #204

Closed
PatrickHofman opened this issue Oct 29, 2020 · 0 comments
Closed

Bug DockingManager not updating state when closing a tab #204

PatrickHofman opened this issue Oct 29, 2020 · 0 comments

Comments

@PatrickHofman
Copy link
Contributor

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants