【问题标题】:Why i cant use my custom WPF control in c# code为什么我不能在 C# 代码中使用我的自定义 WPF 控件
【发布时间】:2010-07-16 04:21:36
【问题描述】:

我是这个论坛的新手。 我有一个使用 c# 和 xaml 定义的自定义用户控件。当我将此控件拖放到 WPF 窗口时,它可以工作。甚至我可以编辑 xaml 代码标签并插入我的控件。但是当我在 c# 代码中使用我的控件时,它不起作用。

这是我的 xaml 控件定义

<ResourceDictionary
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:local="clr-namespace:UserControl"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">   
  <!-- Resource dictionary entries should be defined here. -->
  <Style TargetType="{x:Type local:WellImage}">
    <Setter Property="Focusable" Value="false" />
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type local:WellImage}">
          <Grid Width="Auto" Height="Auto">
            <Ellipse Stroke="{Binding Path=WellBorder, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}"
                     StrokeThickness="{Binding Path=WellBorderThickness, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}"
                     x:Name="Border" Width="Auto" Height="Auto"
                     HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                     Fill="{Binding Path=OuterBackGround, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" />
            <Ellipse StrokeThickness="0" Margin="25,37,25,18" RenderTransformOrigin="0.5,0.5"
                     Fill="{Binding Path=InnerBackGround, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}"/>
          </Grid>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>

这是我的 c# 控件定义

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace UserControl
{
  public class WellImage : System.Windows.Controls.Button
  {
    public static readonly DependencyProperty InnerBackGroundProperty = DependencyProperty.Register("InnerBackGround", typeof(RadialGradientBrush), typeof(WellImage));
    public static readonly DependencyProperty OuterBackGroundProperty = DependencyProperty.Register("OuterBackGround", typeof(RadialGradientBrush), typeof(WellImage));
    public static readonly DependencyProperty WellBorderProperty = DependencyProperty.Register("WellBorder", typeof(SolidColorBrush), typeof(WellImage));
    public static readonly DependencyProperty WellBorderThicknessProperty = DependencyProperty.Register("WellBorderThickness", typeof(double), typeof(WellImage));

    public WellImage()
    {
      // Insert code required on object creation below this point.
      InnerBackGround = (RadialGradientBrush)this.Resources["WellSelectedInnerCircleBrush"];
      OuterBackGround = (RadialGradientBrush)this.Resources["WellSelectedOuterCircleBrush"];
      WellBorder = (SolidColorBrush)this.Resources["NormalBackgroundBrush"];
      WellBorderThickness =2;
    }

    static WellImage()
    {
      //This OverrideMetadata call tells the system that this element wants to provide a style that is different than its base class.
      //This style is defined in themes\generic.xaml
      DefaultStyleKeyProperty.OverrideMetadata(typeof(WellImage), new FrameworkPropertyMetadata(typeof(WellImage)));
    }

    public RadialGradientBrush InnerBackGround
    {
      get { return (RadialGradientBrush)GetValue(InnerBackGroundProperty); }
      set { SetValue(InnerBackGroundProperty, value); }
    }

    public RadialGradientBrush OuterBackGround
    {
      get { return (RadialGradientBrush)GetValue(OuterBackGroundProperty); }
      set { SetValue(OuterBackGroundProperty, value); }
    }

    public SolidColorBrush WellBorder
    {
      get { return (SolidColorBrush)GetValue(WellBorderProperty); }
      set { SetValue(WellBorderProperty, value); }
    }

    public double WellBorderThickness
    {
      get { return (double)GetValue(WellBorderThicknessProperty); }
      set { SetValue(WellBorderThicknessProperty, value); }
    }
  }
}

这就是尝试通过 c# 访问此控件的方式

 WellImage image = new WellImage();            
        image.Height = 40; 
        image.Width = 40;
        image.Margin = new Thickness(30, 30, 30, 30);
        image.VerticalAlignment = VerticalAlignment.Top;
        image.HorizontalAlignment = HorizontalAlignment.Left;
        image.Content = "WellButton";
        grid.Children.Insert(0, image);
        grid.Background = Brushes.LightBlue;
        grid.Width = 120;
        grid.Height = 100;
        grid.VerticalAlignment = VerticalAlignment.Top;
        grid.HorizontalAlignment = HorizontalAlignment.Left;
        gridPartialedMicroPlate.Children.Insert(0, grid);

为什么我无法访问我的控件?

【问题讨论】:

  • 当您尝试以编程方式调用它时收到的错误消息是什么?
  • 嗨彼得,你的代码编译了吗?我的意思是你展示你如何尝试访问它的代码,它编译了吗? IntelliSense 会在您创建图像对象后找到它吗?或者你的意思是如果你使用调试器就永远看不到它的构造?

标签: c# wpf xaml


【解决方案1】:

你有三个主要问题:

  1. 您的 InnerBackground、OuterBackground 和 WellBorder 始终为空,
  2. 您的模板中没有 ContentPresenter,
  3. 您的椭圆大小为零

空笔刷

InnerBackground、OuterBackground 和 WellBorder 为 null 的原因是您在构造函数中将它们设置为 null。例如这一行是一个问题:

InnerBackGround = (RadialGradientBrush)this.Resources["WellSelectedInnerCircleBrush"]; 

这行有两个问题:

  1. 它使用 this.Resources[],它只在本地 ResourceDictionary 中查找。要执行与 StaticResource 等效的操作,您必须查看祖先的 ResourceDictionaries,这意味着调用 FindResource()。
  2. 它在构造函数中,所以它在控件有父控件之前执行。由于此时它没有祖先,因此即使 FindResource() 也不适合您。

这个问题的最佳解决方案是在 Style 中而不是在构造函数中设置这些属性:

<Style TargetType="{x:Type local:WellImage}">
  <Setter Property="Focusable" Value="false" /> 
  <Setter Property="InnerBackground" Value="{DynamicResource WellSelectedInnerCircleBrush}" />
  ...

如果你真的想在构造函数中做,你可以在那里构造一个 DynamicResource 引用:

SetValue(InnerBackgroundProperty,
  new DynamicResourceExtension("WellSelectedInnerCircleBrush")
  .ProvideValue());

缺少 ContentPresenter

如果您在模板中包含了 ContentPresenter,即使省略号是不可见的,您也会看到字符串“WellButton”显示。

您应该考虑修改您的 ControlTemplate 以包含一个 ContentPresenter 以显示 Content 属性。分配 Content 属性但不显示它似乎毫无意义。

零尺寸椭圆

每个椭圆都设置为 Width="Auto" Height="Auto",这对于椭圆意味着零大小。即使有画笔,这也会使您的椭圆不可见。

此外,网格设置为 Width="Auto" Height="Auto",这意味着将其内容压缩到尽可能小的空间中。这意味着如果 WellButton 设置为 VerticalAlignment="Stretch" 或 Horizo​​ntalAlignment="Stretch" 它将顽固地拒绝实际拉伸。这在自定义控件中是一件坏事。

删除您说 Width="Auto" 和 Height="Auto" 的两个地方,而不是设置 Height 和 Width。

其他问题和挑剔

您的第二个椭圆的 StrokeThickness="0" 对我来说毫无意义,因为没有中风。为什么不把它排除在外? RenderTransformOrigin 也一样,因为没有 RenderTransform。

您的第一个椭圆不需要指定 Horizo​​ntalAlignment="Stretch" VerticalAlignment="Stretch" 因为这是默认设置。

你的绑定太复杂了:

  1. 您可以省略“Path=”,除非路径包含附加属性。
  2. 您可以省略“Mode=OneWay”,因为这是默认设置,但最重要的是:
  3. 您可以使用更简洁的 TemplateBinding:

模板绑​​定语法:

<Ellipse Stroke="{TemplateBinding WellBorder}"
         ... />

第二个椭圆的边距非常大,即使 C# 代码中的 Width="40" Height="40" 得到尊重(因为 Auto 问题已解决),Ellipse 仍然没有大小。此外,椭圆的边距如此不寻常似乎也很奇怪。

Nitpick:“Background”是一个英文单词,中间没有大写字母G。

【讨论】:

  • @Peter:很高兴我能帮上忙。顺便说一句,如果您认为这是您问题的最佳答案,则应单击复选标记将其标记为最佳答案。这给了我们两个“代表”点,它带来了你的“答案%”,它帮助其他人识别最佳答案。
猜你喜欢
  • 1970-01-01
  • 2015-06-12
  • 2020-12-11
  • 1970-01-01
  • 2010-12-13
  • 2017-09-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-22
相关资源
最近更新 更多