Home Forums WPF controls Other WPF controls AvalonDock: NullReferenceException when restoring state w/ dynamic Anchorables

Tagged: 

Viewing 4 posts - 1 through 4 (of 4 total)
  • Author
    Posts
  • rabjen-iwes
    Participant
    Post count: 8
    #44509 |

    Hello,

    I have the NullReferenceException problem when restoring state again.

    I derive my anchorables from this class:

    
        public abstract class AbstractPanel {
    
            private Visibility _visibility;
    
            public virtual string ContentId { get { return GetType().AssemblyQualifiedName; } }
    
            public abstract string Title { get; }
    
            public abstract FrameworkElement UIElement { get; }
    
            public Visibility Visibility {
                get {
                    return _visibility;
                }
                set {
                    var oldVis = Visibility;
                    _visibility = value;
                    if(oldVis != Visibility) {
                        OnPropertyChanged("Visibility");
                    }
                }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected void OnPropertyChanged(string propertyName) {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    

    Right at the start, in my App.xaml.cs, I collect my panels (I am using MEF):

    
                foreach(var panel in allServices
                    .OfType<IPanelComponent>()
                    .SelectMany(c => c.Panels)) {
                    SimpleIoc.Default.Register<AbstractPanel>(() => panel, panel.ContentId, true);
                }
    

    In my XAML, I registered it correctly:

    
                        <styles:PanesStyleSelector.AnchorableStyle>
                            <Style TargetType="{x:Type xcad:LayoutAnchorableItem}">
                                <Setter Property="Title" Value="{Binding Model.Title}"/>
                                <Setter Property="ContentId" Value="{Binding Model.ContentId}" />
                                <Setter Property="Visibility" Value="{Binding Model.Visibility, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                            </Style>
                        </styles:PanesStyleSelector.AnchorableStyle>
    

    Right at the start of the ViewModel, I load the anchorables accordingly:

    
    Anchorables = SimpleIoc.Default.GetAllCreatedInstances<AbstractPanel>().ToArray();
    

    When saving a layout, the generated XML looks OK:

    
    <?xml version="1.0" encoding="utf-16"?>
    <LayoutRoot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <RootPanel Orientation="Horizontal">
        <LayoutAnchorablePane DockWidth="188" FloatingWidth="200" FloatingHeight="519" FloatingLeft="87" FloatingTop="454">
          <LayoutAnchorable AutoHideMinWidth="100" AutoHideMinHeight="100" Title="Measurements" IsSelected="True" ContentId="Measurements" FloatingLeft="87" FloatingTop="454" FloatingWidth="200" FloatingHeight="519" LastActivationTimeStamp="01/26/2017 23:10:05" PreviousContainerId="e19e3aa6-8537-4dca-b9c6-d489b756f92d" PreviousContainerIndex="0" />
        </LayoutAnchorablePane>
        <LayoutPanel Orientation="Horizontal">
          <LayoutDocumentPaneGroup Orientation="Horizontal">
            <LayoutDocumentPane />
          </LayoutDocumentPaneGroup>
        </LayoutPanel>
        <LayoutAnchorablePane Id="e19e3aa6-8537-4dca-b9c6-d489b756f92d" DockWidth="200">
          <LayoutAnchorable AutoHideMinWidth="100" AutoHideMinHeight="100" Title="All Channels" IsSelected="True" ContentId="All Channels" LastActivationTimeStamp="01/26/2017 23:10:04" />
        </LayoutAnchorablePane>
      </RootPanel>
      <TopSide />
      <RightSide />
      <LeftSide />
      <BottomSide />
      <FloatingWindows />
      <Hidden />
    </LayoutRoot>
    

    But, when deserializing this (the anchorables list is already populated), I get the NullReferenceException.

    What is going wrong here?

    rabjen-iwes
    Participant
    Post count: 8

    Followup:

    This is the stack trace:

    
    System.NullReferenceException was unhandled by user code
      HResult=-2147467261
      Message=Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.
      Source=Xceed.Wpf.AvalonDock
      StackTrace:
           bei Xceed.Wpf.AvalonDock.Controls.LayoutItem.get_View()
           bei Xceed.Wpf.AvalonDock.DockingManager.RemoveViewFromLogicalChild(LayoutContent layoutContent)
           bei Xceed.Wpf.AvalonDock.DockingManager.DetachAnchorablesSource(LayoutRoot layout, IEnumerable anchorablesSource)
           bei Xceed.Wpf.AvalonDock.DockingManager.OnLayoutChanged(LayoutRoot oldLayout, LayoutRoot newLayout)
           bei Xceed.Wpf.AvalonDock.DockingManager.OnLayoutChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
           bei System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
           bei System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
           bei System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
           bei System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
           bei System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal)
           bei System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value)
           bei Xceed.Wpf.AvalonDock.DockingManager.set_Layout(LayoutRoot value)
           bei Xceed.Wpf.AvalonDock.Layout.Serialization.XmlLayoutSerializer.Deserialize(TextReader reader)
           bei My.App.ViewModel.Behavior.AvalonDockLayoutSerializer.LoadLayout(DockingManager dm, String layoutXml) in C:\Somewhere\MyApp\ViewModel\Behavior\AvalonDockLayoutSerializer.cs:Zeile 108.
           bei My.App.ViewModel.MainViewModel.<>c__DisplayClass3_1.<LoadLayout>b__1() in C:\Somewhere\MyApp\ViewModel\MainViewModel.cs:Zeile 46.
           bei System.Windows.Threading.DispatcherOperation.InvokeDelegateCore()
           bei System.Windows.Threading.DispatcherOperation.InvokeImpl()
      InnerException: 
    

    I got it to work in the meantime, with a crude hack using reflection.

    This is what I do:

    
    
            public static void LoadLayout(this DockingManager dm, string layoutXml) {
                // Walk down the layout and gather the LayoutContent elements.
                // AD bails out when it tries to invoke RemoveViewFromLogicalChild
                // on them.
                var l = GatherLayoutContent(dm.Layout).ToArray();
                // Remove the views by force
                foreach(var x in l) {
                    dm.GetType()
                        .GetMethods(BindingFlags.Instance | BindingFlags.NonPublic)
                        .Where(m => m.Name.Equals("RemoveViewFromLogicalChild"))
                        .First()
                        .Invoke(dm, new object[] { x });
                }
                // Now, the deserialization works
                using(var sr = new StringReader(layoutXml)) {
                    var ser = new XmlLayoutSerializer(dm);
                    ser.Deserialize(sr);
                }
            }
    
            private static IEnumerable<LayoutContent> GatherLayoutContent(ILayoutElement le) {
                if(le is LayoutContent) {
                    yield return (LayoutContent)le;
                }
                IEnumerable<ILayoutElement> children = new ILayoutElement[0];
                if(le is LayoutRoot) {
                    children = ((LayoutRoot)le).Children;
                } else if(le is LayoutPanel) {
                    children = ((LayoutPanel)le).Children;
                } else if(le is LayoutDocumentPaneGroup) {
                    children = ((LayoutDocumentPaneGroup)le).Children;
                } else if(le is LayoutAnchorablePane) {
                    children = ((LayoutAnchorablePane)le).Children;
                } else if(le is LayoutDocumentPane) {
                    children = ((LayoutDocumentPane)le).Children;
                }
                foreach(var child in children) {
                    foreach(var x in GatherLayoutContent(child)) {
                        yield return x;
                    }
                }
            }
    

    Needless to say, I want to get rid of this hack. So, did I find a bug or am I missing something?

    Fawzi [Xceed]
    Member
    Post count: 722

    Hi,

    Can you submit a sample so we can reproduce your issue so that we could have a closer look. You may send it to: support@xceed.com

    rabjen-iwes
    Participant
    Post count: 8

    I just wanted to give a followup that I got it to work now. I didn’t send you demo code because this problem occurred in company code I am not allowed to give away.

    I still don’t know what exactly was the culprit, but I redesigned some parts of my application the following way:

    – I put my Anchorable-specific stuff in an interface IPanel that has all the necessary things like the title, Content ID, and a property referencing a FrameworkElement that displays the actual panel
    – I created an AnchorableViewModel that has a property which is a reference to my IPanel and a Visibility property
    – this AnchorableViewModel is now in the same assembly as the one where I construct the Dock (my former Panel class I directly used as the view model was defined in another assembly)
    – I wire up the AnchorableViewModel accordingly in XAML

    Plus, since I do all my UI stuff highly asynchronously with a lot of async/await and Tasks, and using Dispatcher.InvokeAsync a lot to update my ObservableCollections, I cleaned that part up, too. maybe there was some race condition.

Viewing 4 posts - 1 through 4 (of 4 total)

You must be logged in to reply to this topic.