views:

604

answers:

2

I'm working on a custom panel control and one of the things I'm trying to have it do is swap it's content at run-time. There's two states to this control: Maximized and Normal. When the user clicks a button on the control the state switches. There's two properties on this control: MaximizedContent and MinimizedContent. When the button to swap states is clicked, the Content property of the control needs to swap between MaximizedContent and MinimizedContent. The problem comes when there's bindings inside of the MaximizedContent or MinimizedContent. The don't seem to be part of the "Tree" and therefore binding doesn't work... at least that's my theory. So my question is how do I make them part of the tree?

Here's a simplified example:

MainWindow.xaml

<Window x:Class="SwappingContentTest.MainWindow"
        Loaded="Window_Loaded"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:local="clr-namespace:SwappingContentTest"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"&gt;

    <StackPanel  HorizontalAlignment="Left">
        <Button x:Name="swapContentButton"
                Click="swapContentButton_Click"
                Content="Swap Content" />

        <local:SwappableContentControl x:Name="swappableControl">
            <local:SwappableContentControl.MaximizedContent>
                <StackPanel>
                    <CheckBox x:Name="maximizedCheckBox"
                              Content="Maximized CheckBox" />
                    <Button x:Name="maximizedButton"
                            Content="Maximized Button"
                            IsEnabled="{Binding ElementName=maximizedCheckBox, Path=IsChecked}" />
                </StackPanel>
            </local:SwappableContentControl.MaximizedContent>

            <local:SwappableContentControl.MinimizedContent>
                <StackPanel>
                    <CheckBox x:Name="minimizedCheckBox"
                              Content="Minimized CheckBox" />
                    <Button x:Name="minimizedButton"
                            Content="Minimized Button"
                            IsEnabled="{Binding ElementName=minimizedCheckBox, Path=IsChecked}" />
                </StackPanel>
            </local:SwappableContentControl.MinimizedContent>
        </local:SwappableContentControl>

        <CheckBox x:Name="standardCheckBox"
                  Content="Standard CheckBox"
                  Margin="0,20,0,0"/>
        <Button x:Name="standardButton"
                Content="StandardButton"
                IsEnabled="{Binding ElementName=standardCheckBox, Path=IsChecked}" />
    </StackPanel>
</Window>

MainWindow.cs

namespace SwappingContentTest
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            swappableControl.SwapContent();
        }

        private void swapContentButton_Click(object sender, RoutedEventArgs e)
        {
            swappableControl.SwapContent();
        }
    }
}

SwappableContentControl.cs

namespace SwappingContentTest
{
    public class SwappableContentControl : ContentControl
    {
        public static readonly DependencyProperty MaximizedContentProperty = DependencyProperty.Register("MaximizedContent", typeof(object), typeof(SwappableContentControl));

        public static readonly DependencyProperty MinimizedContentProperty = DependencyProperty.Register("MinimizedContent", typeof(object), typeof(SwappableContentControl));

        public static readonly DependencyProperty StateProperty = DependencyProperty.Register("State", typeof(SwappableContentControlState), typeof(SwappableContentControl),
            new PropertyMetadata(new PropertyChangedCallback(StatePropertyCallback)));

        public static void StatePropertyCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            SwappableContentControl control = (SwappableContentControl)d;
            if ((SwappableContentControlState)e.NewValue == SwappableContentControlState.Maximized)
            {
                control.Content = control.MaximizedContent;
            }
            else
            {
                control.Content = control.MinimizedContent;
            }
        }

        public object MaximizedContent
        {
            get { return GetValue(MaximizedContentProperty); }
            set { SetValue(MaximizedContentProperty, value); }
        }


        public object MinimizedContent
        {
            get { return GetValue(MinimizedContentProperty); }
            set { SetValue(MinimizedContentProperty, value); }
        }


        public SwappableContentControlState State
        {
            get { return (SwappableContentControlState)GetValue(StateProperty); }
            set { SetValue(StateProperty, value); }
        }

        public void SwapContent()
        {
            if (State == SwappableContentControlState.Maximized)
            {
                State = SwappableContentControlState.Normal;
            }
            else
            {
                State = SwappableContentControlState.Maximized;
            }
        }
    }
}

Here's a link to the project: http://www.freewebs.com/thrash505/SwappingContentTest.zip

+1  A: 

I'm having the same problem as you and came up with the same conclusion, unfortunatlely is still haven't found an answer

MatRichard
+3  A: 

I suggest not swapping the content itself, but rather placing two ContentControl instances in your control and changing the visibility. In addition to being cleaner overall, this will have the performance advantage of only updating the control layout and not forcing the trees to be rebuilt. Also it means both ContentControls remain in the logical tree at all times, making them much easier to reference in your control implementation and keeping bindings properly updated. Plus you get the benefit that they can be templated separately, opening the door for nice visual state changes.

AndyM
I thought about doing that a while ago, but for some reason I was thinking it would be slower... mostly because of all the controls from both contents on the screen all at once... but now that I think about it again, the rendering engine, input engine, and those sorts of things probably don't do anything with non-visible controls...I agree with your suggestion and I'll try it out, thanks.
Thrash505
AndyM