WPF - Обмен ContentControl (связывание элементов ???)

2

Я работаю над настраиваемым элементом управления панелью, и одна из вещей, которые я пытаюсь сделать, это обмен файлами во время выполнения. Для этого управления два состояния: Максимизировано и Нормальное. Когда пользователь нажимает кнопку на элементе управления, состояние переключается. В этом элементе управления есть два свойства: MaximizedContent и MinimizedContent. Когда нажата кнопка для переключения состояний, свойство Content элемента управления необходимо поменять между MaximizedContent и MinimizedContent. Проблема возникает, когда привязки внутри MaximizedContent или MinimizedContent. Не похоже, что это часть "Дерева", и поэтому привязка не работает... по крайней мере, моя теория. Итак, мой вопрос: как сделать их частью дерева?

Здесь приведен упрощенный пример:

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

    <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;
            }
        }
    }
}

Здесь ссылка на проект: http://www.freewebs.com/thrash505/SwappingContentTest.zip

  • 0
    Пожалуйста, объясните немного больше, в чем проблема. И постарайтесь свести весь этот код к чему-то более управляемому, есть такие файлы, как App.cs или весь код DependecyProperty, которые можно удалить и облегчить понимание вашей проблемы.
  • 0
    Я сократил часть кода, надеюсь, это поможет. Лучше всего загрузить проект и убедиться сами. У меня есть ContentControl с двумя дополнительными свойствами Content; один для maximzied и один для минимизированного. Я меняю это содержимое в собственное свойство Content во время выполнения. Но, поскольку при загрузке элемента управления нет части реального дерева, привязка не работает, когда я меняю их во время выполнения. Мне нужно выяснить, как сделать их частью дерева, когда я поменяюсь ... Я думаю, что это проблема, но я могу ошибаться.
Показать ещё 1 комментарий
Теги:
wpf
binding
controls

2 ответа

5
Лучший ответ

Я предлагаю не обмениваться содержимым, а размещать два экземпляра ContentControl в вашем элементе управления и изменять видимость. В дополнение к чистоте в целом, это будет иметь преимущество в производительности только обновления макета управления и не заставляя деревья перестраиваться. Также это означает, что и ContentControls всегда остаются в логическом дереве, что упрощает их сопоставление в реализации вашего управления и правильное обновление привязок. Кроме того, вы получаете то преимущество, что их можно планировать отдельно, открывая дверь для приятных визуальных изменений состояния.

  • 0
    Я думал о том, чтобы сделать это некоторое время назад, но по какой-то причине я думал, что это будет медленнее ... в основном из-за одновременного управления всеми элементами содержимого на экране ... но теперь, когда я снова об этом думаю, движок рендеринга, движок ввода и тому подобное, вероятно, ничего не делают с невидимыми элементами управления ... Я согласен с вашим предложением, и я попробую его, спасибо.
  • 0
    Пока вы используете «Свернутый», чтобы скрыть, а не «Скрытый», ваша производительность должна быть в порядке, поскольку это будет только обновлять макет, а не восстанавливать визуальные и логические деревья.
0

В исходном примере после замены содержимого будет создана следующая ошибка привязки:

Ошибка System.Windows.Data: 4: не удается найти источник для привязки с ссылка 'ElementName = maximizedCheckBox'. BindingExpression: Path = IsChecked; DataItem = NULL; целевой элемент 'Button' (Name= 'maximizedButton'); target свойство IsEnabled (тип 'Boolean')

FrameworkElement предоставляет два метода: AddLogicalChild и RemoveLogicalChild

Любые свойства зависимостей "Содержимое" должны предоставлять значение измененного обратного вызова. В этом обработчике обратного вызова сделайте вызов RemoveLogicalChild для старого контента и вызовите AddLogicalChild для нового содержимого. Это сохраняет логическое дерево в синхронизации и поддерживает модель DataContext.

Глядя на исходный пример, новая реализация MinimizedContent будет выглядеть примерно так:

// MinimizedContent
public static readonly DependencyProperty MinimizedContentProperty
    = DependencyProperty.Register("MinimizedContent", typeof(object), typeof(SwappableContentControl),
    new PropertyMetadata(new PropertyChangedCallback(OnMinimizedContentChanged)));

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

private static void OnMinimizedContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    SwappableContentControl swappableContentControl = d as SwappableContentControl;
    if (swappableContentControl == null)
        return;

    swappableContentControl.RemoveLogicalChild(e.OldValue);
    swappableContentControl.AddLogicalChild(e.NewValue);
}

Выполнение вызовов AddLogicalChild и RemoveLogicalChild устраняет проблему, и привязки работают должным образом.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню