2013-12-13 1 views
3

RequirementПОЗВОЛЯЮЩ имени элементы в нескольких содержимого пользовательского элемента управления

Давайте начнем с того, что я пытаюсь достичь. Я хочу иметь сетку с двумя столбцами и разделителем сетки (для этого есть немного больше, но, давайте сделаем это простым). Я хочу иметь возможность использовать эту сетку в разных местах, поэтому вместо того, чтобы создавать ее каждый раз, я хочу создать настраиваемый элемент управления, который содержит два ContentPresenters.

Конечной целью является эффективным, чтобы иметь возможность написать XAML как это:

<MyControls:MyGrid> 
    <MyControls:MyGrid.Left> 
     <Label x:Name="MyLabel">Something unimportant</Label> 
    </MyControls:MyGrid.Left> 
    <MyControls:MyGrid.Right> 
     <Label>Whatever</Label> 
    </MyControls:MyGrid.Right> 
</MyControls:MyGrid> 

ВАЖНО: Обратите внимание на то, что я хочу, чтобы применить Name к моему Label элементу.

Покушение 1

Я сделал много поиска решений, и лучший способ я нашел, чтобы создать UserControl вместе с XAML файл, который определил мою сетку. Этот файл XAML содержал элементы 2 ContentPresenter, и с магией привязки я смог получить что-то хорошее, что было здорово. Однако проблема с этим подходом не будучи в состоянии Name вложенных элементы управления, что приводит к следующей ошибке сборки:

Cannot set Name attribute value 'MyName' on element 'MyGrid'. 'MyGrid' is under the scope of element 'MyControls', which already had a name registered when it was defined in another scope.

С этой ошибкой в ​​руке, я вернулся к доктору Google ...

Покушение 2 (ток)

После намного больше поисков я нашел некоторую информацию здесь, так что предложила эту проблему была из-за наличия связанного XAML файл с MyGrid классом, и эта проблема должна быть разрешима путем удаления XAML и создание всех управления через код в методе OnInitialized.

Итак, я отправился по этому пути и получил все кодирование и компиляцию. Хорошей новостью является то, что теперь я могу добавить Name в свой вложенный Label элемент управления, плохая новость ничего не делает! Не в режиме разработки, а не при запуске приложения. Ошибок не возникает.

Итак, мой вопрос: что мне не хватает? Что я делаю не так?

Я также открыт для предложений по другим способам удовлетворения моих требований.

Текущий код

public class MyGrid : UserControl 
{ 
    public static readonly DependencyProperty LeftProperty = DependencyProperty.Register("Left", typeof(object), typeof(MyGrid), new PropertyMetadata(null)); 
    public object Left 
    { 
     get { return (object)GetValue(LeftProperty); } 
     set { SetValue(LeftProperty, value); } 
    } 

    public static readonly DependencyProperty RightProperty = DependencyProperty.Register("Right", typeof(object), typeof(MyGrid), new PropertyMetadata(null)); 
    public object Right 
    { 
     get { return (object)GetValue(RightProperty); } 
     set { SetValue(RightProperty, value); } 
    } 

    Grid MainGrid; 

    static MyGrid() 
    { 
     DefaultStyleKeyProperty.OverrideMetadata(typeof(MyGrid), new FrameworkPropertyMetadata(typeof(MyGrid))); 
    } 

    protected override void OnInitialized(EventArgs e) 
    { 
     base.OnInitialized(e); 

     //Create control elements 
     MainGrid = new Grid(); 

     //add column definitions 
     ColumnDefinition leftColumn = new ColumnDefinition() 
     { 
      Name = "LeftColumn", 
      Width = new GridLength(300) 
     }; 
     MainGrid.ColumnDefinitions.Add(leftColumn); 

     MainGrid.ColumnDefinitions.Add(new ColumnDefinition() 
     { 
      Width = GridLength.Auto 
     }); 

     //add grids and splitter 
     Grid leftGrid = new Grid(); 
     Grid.SetColumn(leftGrid, 0); 
     MainGrid.Children.Add(leftGrid); 

     GridSplitter splitter = new GridSplitter() 
     { 
      Name = "Splitter", 
      Width = 5, 
      BorderBrush = new SolidColorBrush(Color.FromArgb(255, 170, 170, 170)), 
      BorderThickness = new Thickness(1, 0, 1, 0) 
     }; 
     MainGrid.Children.Add(splitter); 

     Grid rightGrid = new Grid(); 
     Grid.SetColumn(rightGrid, 1); 
     MainGrid.Children.Add(rightGrid); 

     //add content presenters 
     ContentPresenter leftContent = new ContentPresenter(); 
     leftContent.SetBinding(ContentPresenter.ContentProperty, new Binding("Left") { Source = this }); 
     leftGrid.Children.Add(leftContent); 

     ContentPresenter rightContent = new ContentPresenter(); 
     rightContent.SetBinding(ContentPresenter.ContentProperty, new Binding("Right") { Source = this }); 
     rightGrid.Children.Add(rightContent); 

     //Set this content of this user control 
     this.Content = MainGrid; 
    } 
} 
+0

'создавая все управление через код' - Неправильно. Это то, для чего нужны 'ControlTemplate'. –

+0

@HighCore: Спасибо за ваш полезный комментарий ... вы говорите, что у вас есть предложение заменить вторую попытку? – musefan

+0

Вы должны переключить реализацию с 'UserControl' на' CustomControl' и не должно быть проблем с именем. –

ответ

3

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


Отказ от ответственности: Я еще не имеют достаточного опыта работы с WPF, чтобы уверенно сказать, что мое решение является лучшим и/или рекомендуемый способ сделать это, только то, что это, безусловно, работает.


Прежде всего создать новый пользовательский элемент управления: "Add" > "New Item" > "Custom Control (WPF)". Это создаст новый класс, который наследует от Control.

В здесь мы ставим наши свойства зависимостей для привязки к выводимым содержимому предъявителей:

public class MyGrid : Control 
{ 
    public static readonly DependencyProperty LeftProperty = DependencyProperty.Register("Left", typeof(object), typeof(MyGrid), new PropertyMetadata(null)); 
    public object Left 
    { 
     get { return (object)GetValue(LeftProperty); } 
     set { SetValue(LeftProperty, value); } 
    } 

    public static readonly DependencyProperty RightProperty = DependencyProperty.Register("Right", typeof(object), typeof(MyGrid), new PropertyMetadata(null)); 
    public object Right 
    { 
     get { return (object)GetValue(RightProperty); } 
     set { SetValue(RightProperty, value); } 
    } 

    static MyGrid() 
    { 
     DefaultStyleKeyProperty.OverrideMetadata(typeof(MyGrid), new FrameworkPropertyMetadata(typeof(MyGrid))); 
    } 
} 

При добавлении этого файла класс в Visual Studio, он автоматически создаст новый файл «Generic.xaml» в проекте содержащее стиль для этого элемента управления, наряду с шаблоном управления в этом стиле - это то, где мы определяем наши элементы управления ...

<Style TargetType="{x:Type MyControls:MyGrid}"> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type MyControls:MyGrid}"> 
       <Grid> 
        <Grid.ColumnDefinitions> 
         <ColumnDefinition Width="500" /> 
         <ColumnDefinition Width="*" /> 
        </Grid.ColumnDefinitions> 
        <Grid Grid.Column="0"> 
         <ContentPresenter x:Name="LeftContent" /> 
        </Grid> 
        <GridSplitter Width="5" BorderBrush="#FFAAAAAA" BorderThickness="1,0,1,0"> 
        </GridSplitter> 
        <Grid Grid.Column="1"> 
         <ContentPresenter x:Name="RightContent" /> 
        </Grid> 
       </Grid> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

Заключительного шаг, чтобы подключить привязки для 2 предъявителей контента, так назад к файлу класса.

Добавьте следующий метод переопределения для MyGrid класса:

public override void OnApplyTemplate() 
{ 
    base.OnApplyTemplate(); 

    //Apply bindings and events 
    ContentPresenter leftContent = GetTemplateChild("LeftContent") as ContentPresenter; 
    leftContent.SetBinding(ContentPresenter.ContentProperty, new Binding("Left") { Source = this }); 

    ContentPresenter rightContent = GetTemplateChild("RightContent") as ContentPresenter; 
    rightContent.SetBinding(ContentPresenter.ContentProperty, new Binding("Right") { Source = this }); 
} 

И это все! Управления теперь могут быть использованы в другом коде XAML следующим образом:

<MyControls:MyGrid> 
    <MyControls:MyGrid.Left> 
     <Label x:Name="MyLabel">Something unimportant</Label> 
    </MyControls:MyGrid.Left> 
    <MyControls:MyGrid.Right> 
     <Label>Whatever</Label> 
    </MyControls:MyGrid.Right> 
</MyControls:MyGrid> 

Благодаря к @NovitchiS для вашего входа, ваши предложения было жизненно важны в получении такого подхода к работе

Смежные вопросы

 Смежные вопросы