【问题标题】:Custom control can only be instantiated once自定义控件只能实例化一次
【发布时间】:2012-03-08 13:49:50
【问题描述】:

我是 WPF 和自定义控件的新手。

我已经基于 Davids 示例在 VB 中使用 WPF 和 MVVM 实现了一个自定义控件:http://davidowens.wordpress.com/2009/02/18/wpf-search-text-box/

我有一个自定义控件库和一个对我的项目的引用来实现我的控件。 我通过 XAML 在视图中实现了我的控制:

编辑:

<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:l="clr-namespace:UIControls;assembly=UIControls"
mc:Ignorable="d"
x:Class="LoginView"
x:Name="LoginView"
Width="457" Height="216" Visibility="Visible" xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors">

<Grid x:Name="LayoutRoot">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="0.7*"/>
        <ColumnDefinition Width="3*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>

    <l:ExpandedTextBox Name="UsernameText" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Margin="10,0,5,5" />

</Grid>

当我在一切正常后实例化我的自定义控件时,

 <l:ExpandedTextBox Name="UsernameText" />

但是当我尝试在同一个视图中实例化它两次时,我得到一个错误代码:

Error 1 Cannot create an instance of "ExpandedTextBox"

编辑:

出现错误的 XAML 代码如下所示:

<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:l="clr-namespace:UIControls;assembly=UIControls"
mc:Ignorable="d"
x:Class="LoginView"
x:Name="LoginView"
Width="457" Height="216" Visibility="Visible" xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors">

<Grid x:Name="LayoutRoot">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="0.7*"/>
        <ColumnDefinition Width="3*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>

   <l:ExpandedTextBox Name="UsernameText" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Margin="10,0,5,5" />

   <l:ExpandedTextBox Name="Password" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" Margin="10,0,5,5" />

</Grid>

我将第二个自定义控件命名为不同的,而我的第一个自定义控件仍然可以正常工作。 该错误仅出现在第二个自定义控件“密码”中。

对应的 Generic.xaml 文件如下所示:

<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:UIControls">

<SolidColorBrush x:Key="ExpandedTextBox_Background" Color="White" />
<SolidColorBrush x:Key="ExpandedTextBox_Foreground" Color="Black" />
<LinearGradientBrush x:Key="ExpandedTextBox_Border" StartPoint="0,0" EndPoint="0,1">
    <GradientStop Color="#FFABADB3" Offset="0.05" />
    <GradientStop Color="#FFE2E3EA" Offset="0.07" />
    <GradientStop Color="#FFE3E9EF" Offset="1" />
</LinearGradientBrush>
<LinearGradientBrush x:Key="ExpandedTextBox_BorderMouseOver" StartPoint="0,0" EndPoint="0,1">
    <GradientStop Color="#FF5C97C1" Offset="0.05" />
    <GradientStop Color="#FFB9D7EB" Offset="0.07" />
    <GradientStop Color="#FFC7E2F1" Offset="1" />
</LinearGradientBrush>
<SolidColorBrush x:Key="ExpandedTextBox_SearchIconBorder" Color="White" />
<SolidColorBrush x:Key="ExpandedTextBox_SearchIconBackground" Color="White" />
<LinearGradientBrush x:Key="ExpandedTextBox_SearchIconBorder_MouseOver" StartPoint="0,0" EndPoint="0,1" >
    <GradientStop Color="#FFFFFFFF" Offset="0" />
    <GradientStop Color="#FFE5F4FC" Offset="1" />
</LinearGradientBrush>
<LinearGradientBrush x:Key="ExpandedTextBox_SearchIconBackground_MouseOver" StartPoint="0,0" EndPoint="0,1" >
    <GradientStop Color="#FFE7F5FD" Offset="0" />
    <GradientStop Color="#FFD2EDFC" Offset="0.5" />
    <GradientStop Color="#FFB6E3FD" Offset="0.51" />
    <GradientStop Color="#FF9DD5F3" Offset="1" />
</LinearGradientBrush>
<LinearGradientBrush x:Key="ExpandedTextBox_SearchIconBorder_MouseDown" StartPoint="0,0" EndPoint="0,1" >
    <GradientStop Color="#FFFFFFFF" Offset="0" />
    <GradientStop Color="#FFE5F4FC" Offset="1" />
</LinearGradientBrush>
<LinearGradientBrush x:Key="ExpandedTextBox_SearchIconBackground_MouseDown" StartPoint="0,0" EndPoint="0,1" >
    <GradientStop Color="#FFE7F5FD" Offset="0" />
    <GradientStop Color="#FFD2EDFC" Offset="0.5" />
    <GradientStop Color="#FFB6E3FD" Offset="0.51" />
    <GradientStop Color="#FF9DD5F3" Offset="1" />
</LinearGradientBrush>
<SolidColorBrush x:Key="ExpandedTextBox_LabelTextColor" Color="Gray" />



<Style x:Key="{x:Type l:ExpandedTextBox}" TargetType="{x:Type l:ExpandedTextBox}">
    <Setter Property="Background" Value="{StaticResource ExpandedTextBox_Background}" />
    <Setter Property="BorderBrush" Value="{StaticResource ExpandedTextBox_Border}" />
    <Setter Property="Foreground" Value="{StaticResource ExpandedTextBox_Foreground}" />
    <Setter Property="BorderThickness" Value="1" />
    <Setter Property="SnapsToDevicePixels" Value="True" />
    <Setter Property="LabelText" Value="Username" />
    <Setter Property="FontSize" Value="16" />
    <Setter Property="Text" Value="" />
    <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
    <Setter Property="LabelTextColor" Value="{StaticResource ExpandedTextBox_LabelTextColor}" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type l:ExpandedTextBox}">

                <Border x:Name="Border"
                   Padding="2"
                   Background="{TemplateBinding Background}"
                   BorderBrush="{TemplateBinding BorderBrush}"
                   BorderThickness="{TemplateBinding BorderThickness}">

                    <Grid x:Name="LayoutGrid">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ActualHeight}" />
                        </Grid.ColumnDefinitions>
                        <ScrollViewer x:Name="PART_ContentHost" Grid.Column="0" />

                        <Label x:Name="LabelText"
                          Grid.Column="0"
                          Foreground="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=LabelTextColor}"
                          Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=LabelText}"
                          Padding="2,0,0,0"
                          FontStyle="Italic" />

                        <Border x:Name="PART_SearchIconBorder"
                          Grid.Column="2"
                          Visibility="Collapsed"
                          BorderThickness="1"
                          Padding="1"
                          VerticalAlignment="Stretch"
                          HorizontalAlignment="Stretch"
                          BorderBrush="{StaticResource ExpandedTextBox_SearchIconBorder}"
                          Background="{StaticResource ExpandedTextBox_SearchIconBackground}">

                            <Image x:Name="SearchIcon"
                                Stretch="None"
                                Width="Auto"
                                Height="Auto"
                                HorizontalAlignment="Center"
                                VerticalAlignment="Center"
                                Source="pack://application:,,,/UIControls;component/Images/clear.png" />

                        </Border>
                    </Grid>
                </Border>

                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="BorderBrush" Value="{StaticResource ExpandedTextBox_BorderMouseOver}" />
                    </Trigger>
                    <Trigger Property="IsKeyboardFocusWithin" Value="True">
                        <Setter Property="BorderBrush" Value="{StaticResource ExpandedTextBox_BorderMouseOver}" />
                    </Trigger>
                    <Trigger Property="HasText" Value="True">
                        <Setter Property="Visibility" TargetName="LabelText" Value="Hidden" />
                        <Setter Property="Visibility" TargetName="PART_SearchIconBorder" Value="Visible" />
                    </Trigger>
                    <Trigger Property="IsMouseOver" SourceName="PART_SearchIconBorder" Value="True">
                        <Setter Property="HasClicked" Value="True" />
                        <Setter Property="Background" TargetName="PART_SearchIconBorder" Value="#FFF0F5" />
                    </Trigger>

                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
</ResourceDictionary>

ExpandedTextBox VB 文件如下所示:

Imports System.Windows.Controls.Primitives
Imports System.ComponentModel


Public Class ExpandedTextBox
Inherits System.Windows.Controls.TextBox


Public Property LabelTextProperty As DependencyProperty = DependencyProperty.Register("LabelText", GetType(String), GetType(ExpandedTextBox))
Public Property LabelTextColorProperty As DependencyProperty = DependencyProperty.Register("LabelTextColor", GetType(Brush), GetType(ExpandedTextBox))
Public Property HasTextPropertyKey As DependencyProperty = DependencyProperty.Register("HasText", GetType(Boolean), GetType(ExpandedTextBox), New PropertyMetadata())
Public Property HasTextProperty As DependencyProperty = HasTextPropertyKey
Public Property HasClickedPropertyKey As DependencyProperty = DependencyProperty.Register("HasClicked", GetType(Boolean), GetType(ExpandedTextBox), New PropertyMetadata())
Public Property HasClickedProperty As DependencyProperty = HasClickedPropertyKey

Public Property HasClicked As Boolean
    Get
        Return CType(GetValue(HasClickedProperty), Boolean)
    End Get
    Set(ByVal value As Boolean)
        SetValue(HasClickedProperty, value)
    End Set
End Property
Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Input.MouseButtonEventArgs)
    If HasClicked = True Then
        Me.Text = ""
    Else
        MyBase.OnMouseDown(e)
    End If

End Sub


Public Property LabelText As String
    Get
        Return CType(GetValue(LabelTextProperty), String)
    End Get
    Set(ByVal value As String)
        SetValue(LabelTextProperty, value)
    End Set
End Property

Public Property LabelTextColor As Brush
    Get
        Return CType(GetValue(LabelTextColorProperty), Brush)
    End Get
    Set(ByVal value As Brush)
        SetValue(LabelTextColorProperty, value)
    End Set
End Property

Public Property HasText As Boolean
    Get
        Return CType(GetValue(HasTextProperty), Boolean)
    End Get
    Set(ByVal value As Boolean)
        SetValue(HasTextProperty, value)
    End Set
End Property


Shared Sub New()
    DefaultStyleKeyProperty.OverrideMetadata(GetType(ExpandedTextBox), New FrameworkPropertyMetadata(GetType(ExpandedTextBox)))
End Sub

Protected Overrides Sub OnTextChanged(ByVal e As TextChangedEventArgs)
    MyBase.OnTextChanged(e)
    HasText = Text.Length <> 0
End Sub


End Class

为什么我不能在我的视图中创建两次相同的自定义控件?
提前致谢!

【问题讨论】:

  • 没有理由可以将任意数量的用户控件副本添加到窗口中。这就是用户控制的重点!这里没有足够的代码来告诉你问题可能是什么。
  • 您发布了很多信息,但没有包含引发错误的 XAML。
  • 你可能在构造函数中做了一些你不能在同一个appdomain中做两次的事情。在 Visual Studio 的第二个实例中打开 UIControls.vbproj 并将调试器附加到第一个实例,然后尝试再次将两个 ExpandedTextBoxes 放在同一个视图上。
  • 我用所有代码编辑了这个问题。希望这有助于理解有问题的人。再次提前感谢!
  • 不知道有没有关系,但是依赖属性定义不应该是静态/共享的吗?

标签: wpf vb.net xaml mvvm custom-controls


【解决方案1】:

这可能是因为您指定了ExpendedTextBox UserControl 的x:Name 属性。

您要向其中添加控件的网格知道它们的名称,这就是它在其子集合中检测到具有相同 x:Name 的多个控件时抛出异常的原因。

【讨论】:

  • 我们实际上解决了这个问题。我们有相同的想法 EvAlex,但无法用它解决问题。相反,真正的问题是依赖属性,它们没有设置为共享!
【解决方案2】:

我们通过 Rich 的评论解决了这个问题。谢谢!

问题在于依赖属性,它没有在 VB 文件中设置为共享。新的工作文件如下所示。

Imports System.Windows.Controls.Primitives
Imports System.ComponentModel


Public Class ExpandedTextBox
Inherits System.Windows.Controls.TextBox


Public Shared Property LabelTextProperty As DependencyProperty
Public Shared Property LabelTextColorProperty As DependencyProperty
Public Shared Property HasTextPropertyKey As DependencyProperty
Public Shared Property HasTextProperty As DependencyProperty
Public Shared Property HasClickedPropertyKey As DependencyProperty
Public Shared Property HasClickedProperty As DependencyProperty


Shared Sub New()
    DefaultStyleKeyProperty.OverrideMetadata(GetType(ExpandedTextBox), New FrameworkPropertyMetadata(GetType(ExpandedTextBox)))
    LabelTextProperty = DependencyProperty.Register("LabelText", GetType(String), GetType(ExpandedTextBox))
    LabelTextColorProperty = DependencyProperty.Register("LabelTextColor", GetType(Brush), GetType(ExpandedTextBox))
    HasTextPropertyKey = DependencyProperty.Register("HasText", GetType(Boolean), GetType(ExpandedTextBox), New PropertyMetadata())
    HasTextProperty = HasTextPropertyKey
    HasClickedPropertyKey = DependencyProperty.Register("HasClicked", GetType(Boolean), GetType(ExpandedTextBox), New PropertyMetadata())
    HasClickedProperty = HasClickedPropertyKey
End Sub


Public Property HasClicked As Boolean
    Get
        Return CType(GetValue(HasClickedProperty), Boolean)
    End Get
    Set(ByVal value As Boolean)
        SetValue(HasClickedProperty, value)
    End Set
End Property
Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Input.MouseButtonEventArgs)
    If HasClicked = True Then
        Me.Text = ""
    Else
        MyBase.OnMouseDown(e)
    End If

End Sub


Public Property LabelText As String
    Get
        Return CType(GetValue(LabelTextProperty), String)
    End Get
    Set(ByVal value As String)
        SetValue(LabelTextProperty, value)
    End Set
End Property

Public Property LabelTextColor As Brush
    Get
        Return CType(GetValue(LabelTextColorProperty), Brush)
    End Get
    Set(ByVal value As Brush)
        SetValue(LabelTextColorProperty, value)
    End Set
End Property

Public Property HasText As Boolean
    Get
        Return CType(GetValue(HasTextProperty), Boolean)
    End Get
    Set(ByVal value As Boolean)
        SetValue(HasTextProperty, value)
    End Set
End Property



Protected Overrides Sub OnTextChanged(ByVal e As TextChangedEventArgs)
    MyBase.OnTextChanged(e)
    HasText = Text.Length <> 0
End Sub


End Class

感谢大家的贡献!

很棒的 StackOverflow !

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-01-08
    • 1970-01-01
    • 2010-10-06
    • 2017-07-07
    • 2012-06-11
    • 2018-01-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多