Inject controls in derived windows breaks xaml markup in combination with binding

StackOverflow https://stackoverflow.com/questions/23263741

  •  08-07-2023
  •  | 
  •  

質問

I am trying to inject a LayoutGrid and a canvas into my windows, but this causes a little headache:

Here is my WindowBase class:

public class WindowBase : Window
{
    protected override void OnInitialized(EventArgs e)
    {
        base.OnInitialized(e);

        if (Content is FrameworkElement)
        {
            var originalContent = Content as FrameworkElement;
            var grid = new Grid();
            grid.DataContext = originalContent.DataContext;
            Content = grid;
            grid.Children.Add(originalContent);

            var canvas = new Canvas() { HorizontalAlignment = HorizontalAlignment.Stretch, VerticalAlignment = VerticalAlignment.Stretch };
            grid.Children.Add(canvas);
        }
    }
}

My MainWindow thats inheriting from WindowBase looks like this:

XAML:

<local:WindowBase x:Class="InsertCanvasTest.MainWindow"
                  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                  xmlns:local="clr-namespace:InsertCanvasTest"
                  DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Border>
        <Grid>
            <ComboBox SelectedIndex="1"
                      ItemsSource="{Binding ItemSource1}" />
        </Grid>
    </Border>
</local:WindowBase>

Code Behind of MainWindow:

public partial class MainWindow : WindowBase
{
    private List<int> _itemSource1;
    public List<int> ItemSource1
    {
        get
        {
            if (_itemSource1 == null)
                _itemSource1 = new List<int>(){1,2,3};
            return _itemSource1;
        }

    }

    public MainWindow()
    {
        InitializeComponent();
    }
}

As you can see in my XAML I have specified that the SelectedIndex should be 1, but with the code in my WindowBase where I am trying to inject the Canvas and the Grid this information gets lost and the SelectedIndex is at -1.

Is there a way to fix this?

I would like to keep the MainWindow as a Window and not implement it as a control and load this into some different Window inside a ContentPresenter of so.

I know this problem wouldnt exist if I declared the Canvas/Grid in the XAML directly instead of trying to inject it in codebehind, but doing this with 100+ windows and trying to maintain them if something changes is annoying.

役に立ちましたか?

解決

Change your WindowBase class like that :

WindowBase

[ContentProperty("InternalContent")]
public class WindowBase : Window
{
    // InternalContent
    public static readonly DependencyProperty InternalContentProperty = 
        DependencyProperty.Register( "InternalContent", typeof(object),
        typeof(WindowBase), new FrameworkPropertyMetadata(null));

    public object InternalContent
    {
        get { return GetValue(InternalContentProperty); }
        set { SetValue(InternalContentProperty, value); }
    }

    ...
}

<Window ...>
    <Grid>
        ...
        <ContentControl IsTabStop="false"
            Content="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=InternalContent}" />
        <Canvas />
        ...
    </Grid>
</Window>

In essence, what it does is to create a new InternalContent property that subclasses will see as the default content (thanks to ContentPropertyAttribute), and display that content with a ContentControl.

That said, there is most likely better ways to do what you're trying to do than inheritance. Using templates comes to mind. Or maybe Adorners if what you want is an "above all layer to display things" like your Canvas suggest.

他のヒント

I suspect your problem is the setting of Content property of your Superclass Window to a "new" grid and then inserting the original content into that grid.

Where I think the problem may be is that you are setting the variable originalContent equal to Content BUT Content is an Object meaning it's a Reference Type. Problem shown below

var originalContent = Content as FrameworkElement;
//Reference Type: originalContent POINTS AT Content;
var grid = new Grid();

Content = grid;
//Reference Type: Content POINTS AT grid vsv. originalContent now POINTS AT grid
// Now because of Pointers and Reference Types
// originalContent = grid

If you want to preserve the Original Content in your Window Base Class using the Code you've shared, you would need to clone the control and use that reference in var originalContent = CLONE OF CONTENT.

your variable originalContent has a parent and so kannt be added to a new Frameworkelement. You must remove originalContent from the old parent before you add it to a new one.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top