2009-07-08 2 views
2

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

Вот упрощенный пример:

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 во время выполнения.Но, так как там нет части фактического дерева при загрузке элемента управления, привязка не работает, как только я поменяю их во время выполнения. Мне нужно выяснить, как сделать их частью дерева, когда я поменяю ... Я думаю, что это проблема, но я могу ошибаться. – Thrash505

+0

Любые идеи по этому поводу? – Thrash505

ответ

5

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

+0

Я думал об этом некоторое время назад, но по какой-то причине я думал, что это будет медленнее ... в основном из-за всех элементов управления из обоих содержимого на экране все сразу ... но теперь, когда я думаю об этом снова , движок рендеринга, механизм ввода и те виды вещей, вероятно, ничего не делают с невидимыми элементами управления ... Я согласен с вашим предложением, и я попробую его, спасибо. – Thrash505

+0

До тех пор, пока вы используете «Свернутый», чтобы скрыть, а не «Скрытый», ваша производительность должна быть в порядке, поскольку это будет только обновлять макет, а не восстанавливать визуальные и логические деревья. – AndyM

0

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

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=maximizedCheckBox'. BindingExpression:Path=IsChecked; DataItem=null; target element is 'Button' (Name='maximizedButton'); target property is 'IsEnabled' (type '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 решает эту проблему и привязок работать, как ожидалось.