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?
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?