【问题标题】:Create the same border type around all my controls在我的所有控件周围创建相同的边框类型
【发布时间】:2009-11-07 13:43:07
【问题描述】:

我尝试创建自己的 Border 类,然后将其插入到我的控件中,但似乎我无法为边框内的所有内容分配名称:

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

我该如何解决这个问题?我可以为此使用模板吗?

编辑: 对不起,我不清楚。是的,我用我自己的 XAML 文件对 Border 进行了子类化,并在上面的代码中得到了这个编译器错误:

错误 2 无法设置名称属性 元素上的值“ifBlock” '堆栈面板'。 '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>

【问题讨论】:

  • 请提供更多详细信息。你是继承Border还是自己滚动?是编译时的错误还是运行时的错误?如果您实际上不尝试使用该名称,它会编译/运行吗?

标签: .net wpf xaml subclass border


【解决方案1】:

通过引入代码隐藏,您创建了一个额外的 NameScope。您可以在运行时通过在调用 InitializeComponent() 后清除 NameScope 来移除这个额外的 NameScope:

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 或 theme/Generic.xaml 中定义的那个

请注意,您可以只将样式放在 app.xaml 的 ResourceDictionary 中,而不是创建单独的文件。

使用它与使用原始 ElementBorder 相同:

<my:ElementBorder>
  <StackPanel  ...

没有子类

这需要更少的代码。像以前一样在 ResourceDictionary 中添加一个 Style ,除了给它一个 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 的 Child 属性,以便当它发生变化时,绑定可以添加到新 Child 并从旧 Child(如果有)中删除。

我不建议你这样做,除非你真的需要。在您的特定情况下,您可能可以手动绑定子控件或创建包含边框和子控件的模板。正如我所描述的那样,这些中的任何一个都是创建附加属性的更好的解决方案。

【讨论】:

  • 您提供的前两种方法不起作用,我得到了同样的错误。我需要将边框的背景和边框笔刷以及边框内的孩子的背景设置为相同的颜色(我的所有控件的颜色都不同)。样式可以做到这一点吗?
  • 我不理解你。如果您希望每个控件具有不同的颜色,您将在哪里获得该颜色?数据绑定?如果是这样,您可以将绑定表达式放在样式中。在任何情况下,对于您的简单情况,您可以将 Border.BorderBrush 和 child.Background 设置为 Brushes.Transparent,这将导致只有 Border 的背景可见。这看起来非常类似于将所有三个设置为相同的颜色(边框会稍小,因为不会有笔划)。
  • 您是说完全删除 ElementBorder 类并使用 会导致名称范围错误(即它会影响 StackPanel 的名称),但如果您做同样的事情,但不要使用 Style= 属性?还是我在这里也误会了你?
  • 是的,你误解了我的意思。使用 SetNameScope 和 OverrideMetadata 没有修复错误。您的第三个提案“没有子类”效果很好,但我需要一个新的单一属性,将边框的 Background & BorderBrush 及其子类的背景设置为相同的颜色。当然我可以手动完成。
  • 已在问题正文中回答。
【解决方案2】:

通过搜索逻辑树找到名称范围。

如果您滚动自己的 Border(而不是继承 Border 类),名称范围问题可能由以下原因引起:

  • 调用 AddLogicalChild 失败
  • 未正确覆盖 LogicalChildren 枚举

另一方面,如果您将现有的 Border(或任何装饰器)子类化,它将处理逻辑树。

如果在您的边框上设置了明确的 NameScope,也可能会导致这种情况。在这种情况下,将分配名称,但 FindName 不会在 Window/UserContol/template 级别找到它。

发布您的 ElementBorder 课程的相关部分可能有助于我们为您提供更好的答案。

【讨论】:

  • 感谢您的回答。我编辑了我的问题,以便为您提供更多信息。
【解决方案3】:

这被称为 Namescope 问题查看此处了解更多详细信息: http://dotnet.dzone.com/news/c-and-wpf-namescope-my-name-is

【讨论】:

  • 感谢您的链接,但我仍然不确定如何解决我的问题。文章建议调用container.RegisterName("myname", button),但是在xaml中怎么做才能生成变量“myname”呢?
  • 首先需要确定应用的模板,通过获取应用模板所在控件的Template属性值。然后,调用 FindName 的模板版本,将应用模板的控件作为第二个参数传递。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-21
  • 2018-07-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多