Создайте один и тот же тип границы вокруг всех моих элементов управления

Я попытался создать свой собственный класс Border, а затем вставить его в свои элементы управления, но тогда кажется, что я не могу присвоить имена всему, что находится внутри границ:

..
<my:ElementBorder>
        <StackPanel Name="ifBlock" Background="#E0E8FF" />
</my:ElementBorder> 
..

Как я могу обойти это? Могу ли я как-то использовать шаблоны для этого?

РЕДАКТИРОВАТЬ: Извините, что я был неясен. Да, я создал подкласс Border с помощью своего собственного файла XAML и получил эту ошибку компилятора с кодом выше:

Ошибка 2. Не удается установить значение атрибута имени «ifBlock» для элемента «StackPanel». «StackPanel» находится в области действия элемента «ElementBorder», у которого уже было зарегистрировано имя, когда оно было определено в другой области.

Содержимое моего класса ElementBorder не очень интересно, но я все равно его опубликую:

<Border x:Class="DVPE.ElementBorder"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    BorderThickness="4" 
    CornerRadius="4">
</Border>

person codymanix    schedule 07.11.2009    source источник
comment
Пожалуйста, подробнее. Вы создали подкласс Border или создали свой собственный? Ошибка во время компиляции или во время выполнения? Компилируется/запускается ли он, если вы на самом деле не пытаетесь использовать это имя?   -  person Ray Burns    schedule 07.11.2009


Ответы (3)


Внедрив программный код, вы создали дополнительный NameScope. Вы можете удалить этот дополнительный NameScope во время выполнения, очистив NameScope после вызова InitializeComponent():

public ElementBorder()
{
  InitializeComponent();
  NameScope.SetNameScope(this, null);
  ...
}

Хотя это сработает, это не лучшее решение. Вам лучше создать стиль, который UserControl.

Есть два способа сделать это: С подклассом и без.

С подклассом

Создайте свой подкласс ElementBorder и переопределите стиль по умолчанию. Не вызывайте InitializeComponent():

public class ElementBorder
{
  static ElementBorder
  {
    DefaultStyleKeyProperty.OverrideMetadata(typeof(ElementBorder), new FrameworkPropertyMetadata(typeof(ElementBorer)));
  }
  // any additional implementation
}

В вашем файле xaml вместо включения тега создайте ResourceDictionary, содержащий стиль с нужными настройками:

<ResourceDictionary xmlns=... >

  <Style TargeType="{x:Type my:ElementBorder}">
    <Setter Property="BorderThickness" Value="4" />
    ...

Объедините этот ResourceDictionary со словарем, определенным в app.xaml или themes/Generic.xaml, используя тег ‹MergeDictionary>.

Обратите внимание, что вы можете просто поместить стиль в ResourceDictionary в своем app.xaml вместо создания отдельного файла.

Использование этого идентично использованию исходного ElementBorder:

<my:ElementBorder>
  <StackPanel  ...

Без подкласса

Это требует меньше кода. Просто поместите стиль в ResourceDictionary, как и раньше, за исключением того, что дайте ему x:Key и используйте Border в качестве целевого типа:

<ResourceDictionary>

  <Style x:Key="ElementBorderStyle" TargeType="{x:Type Border}">
    <Setter Property="BorderThickness" Value="4" />
    ...

Это можно использовать следующим образом:

 <Border Style="{StaticResource ElementBorderStyle}">
   <StackPanel ...

Если вы хотите, чтобы все ваши границы имели новый стиль, это еще проще. Просто опустите x:Key и используйте Border в качестве TargetType:

<ResourceDictionary>

  <Style TargeType="{x:Type Border}">
    <Setter Property="BorderThickness" Value="4" />
    ...

Это приведет к тому, что все границы получат стиль, поэтому вы можете просто написать:

<Border>
  <StackPanel ...

Ответ на дополнительный вопрос в комментарии ниже

Чтобы установить BorderBrush так же, как фон:

  <Style TargeType="{x:Type Border}">
    <Setter Property="BorderBrush" Value="{Binding Background, RelativeSource={RelativeSource Self}}" />
    ...

Чтобы установить фон дочернего элемента на фон границы: обычно это не требуется, поскольку, пока цвет фона дочернего элемента не установлен, цвет фона границы будет просвечиваться. Единственным исключением являются ситуации с отрицательным полем или RenderTransform, когда дочерний элемент не отображается полностью внутри содержащей границы. В этом случае вам нужно будет привязать фон ребенка к фону границы. Это невозможно сделать в стиле, примененном к BorderBrush, без использования прикрепленного свойства. Но если вы можете делать это без стиля:

<Border x:Name="myBorder">  <!-- Style applied here -->
  <StackPanel Background="{Binding Background, ElementName=myBorder}" ...

это также можно сделать с безымянной границей с {Binding Background, RelativeSource={RelativeSource FindAncestor,Border,1}.

Если вы хотите сделать это полностью в стиле, вам придется добавить немного кода. По сути, создайте класс с прикрепленным свойством с именем вроде «MyBindingTools.BindChildBackgroundToMyBackground». Добавьте PropertyChangedCallback, чтобы, когда для этого свойства установлено значение «true», на дочернем элементе создавалась привязка, в основном:

BindingOperations.SetBinding(border, BackgroundProperty, new Binding("Background") { Source = this });

Кроме того, вам нужно будет следить за свойством Border's Child, чтобы при его изменении привязка могла быть добавлена ​​к новому дочернему элементу и удалена из старого дочернего элемента (если есть).

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

person Ray Burns    schedule 08.11.2009
comment
Первые два метода, которые вы предоставили, не сработали, я получил ту же ошибку. Мне нужно установить Background & BorderBrush границы и фон дочернего элемента внутри границы одного и того же цвета (этот цвет отличается для всех моих элементов управления). Возможно ли это со стилями? - person codymanix; 09.11.2009
comment
Я не понимаю тебя. Если вы хотите, чтобы каждый элемент управления был другого цвета, где вы получите этот цвет? Привязка данных? Если это так, вы можете поместить выражение привязки в стиль. В любом случае, для вашего простого случая вы можете установить Border.BorderBrush и child.Background в Brushes.Transparent, что приведет к тому, что будет виден только фон границы. Это будет очень похоже на настройку всех трех одинаковых цветов (граница будет немного меньше, так как обводки не будет). - person Ray Burns; 09.11.2009
comment
Вы говорите, что полное удаление вашего класса ElementBorder и использование ‹Border Style={StaticResource ElementBorderStyle}› вызывает ошибку области имени (т. е. влияет на имя StackPanel), но это работает нормально, если вы делаете то же самое, но не используете Style= атрибут? Или я и здесь вас неправильно понимаю? - person Ray Burns; 09.11.2009
comment
Да вы меня неправильно поняли. Использование SetNameScope и OverrideMetadata не устранило ошибку. Ваше третье предложение Без подкласса сработало хорошо, но мне нужно новое отдельное свойство, которое устанавливает для Background & BorderBrush границы и Background его дочернего элемента один и тот же цвет. Конечно, я мог бы сделать это вручную. - person codymanix; 10.11.2009
comment
Ответ в теле вопроса. - person Ray Burns; 10.11.2009

Области имен находятся путем поиска в логическом дереве.

Если вы свернули свой собственный Border (в отличие от подкласса класса Border), проблемы с областью имен могут быть вызваны:

  • Не удалось вызвать AddLogicalChild
  • Неправильное переопределение перечисления LogicalChildren

С другой стороны, если вы создадите подкласс существующей границы (или любого декоратора), он будет обрабатывать логическое дерево.

Это также может быть вызвано тем, что на вашей границе установлен явный NameScope. В этом случае имя будет назначено, но FindName не найдет его на уровне окна/пользователя/шаблона.

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

person Ray Burns    schedule 07.11.2009
comment
Спасибо за ответы. Я отредактировал свой вопрос, чтобы дать вам больше информации. - person codymanix; 08.11.2009

Это известно как проблема Namescope. Подробнее см. здесь: http://dotnet.dzone.com/news/c-and-wpf-namescope-my-name-is

person viky    schedule 08.11.2009
comment
Спасибо за ссылку, но я все еще не уверен, как решить мою проблему. В статье предлагалось вызывать container.RegisterName(myname, button), но как это сделать в xaml, чтобы он генерировал переменную myname? - person codymanix; 08.11.2009
comment
Сначала необходимо определить применяемый шаблон, получив значение свойства Template элемента управления, к которому применяется шаблон. Затем вы вызываете версию шаблона FindName, передавая элемент управления, к которому был применен шаблон, в качестве второго параметра. - person viky; 08.11.2009