WPF用户控件数据绑定未生效

4
我正在创建一个简单的用户控件,它将弹出窗口和文本视图结合在一起,没有太复杂的东西。当我最初将其设置在窗口中以对其进行样式设计时,它完美地工作了,但是当我将其移动到用户控件中去实际完成它时,它不再正确工作。
我将一个最小值和最大值传递给该控件,然后它会自动创建一个在该范围内选择的数字列表。在用户控件中,数字列表无法正确绑定,谁知道为什么。也许有人可以看一下我的代码。
我已经阅读了许多其他关于此问题的问题,但不知道发生了什么。在输出窗口中没有获得任何错误信息,因此没有线索。无论如何,以下是代码 -
UserControl.xaml
<UserControl x:Class="UserControl1"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         x:Name="Me"
         DataContext="{Binding RelativeSource={RelativeSource Self}}">

<StackPanel>
    <TextBox Name="myTextbox"
             Height="30"
             Margin="0"
             FontSize="14"
             IsReadOnly="True"
             Padding="5,2"
             Text="{Binding Value}" />
    <Popup x:Name="myPopup"
           PlacementTarget="{Binding ElementName=myTextbox}"
           StaysOpen="True">
        <Popup.Style>
            <Style TargetType="{x:Type Popup}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding ElementName=myTextbox, Path=IsFocused}" Value="True">
                        <Setter Property="IsOpen" Value="True" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Popup.Style>

        <StackPanel>
            <ListView Name="myListView"
                      Height="100"
                      MaxHeight="300"
                      ItemsSource="{Binding List,
                                            UpdateSourceTrigger=PropertyChanged}"
                      SelectionChanged="ListView_SelectionChanged">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <Label Width="100"
                               Height="30"
                               Margin="0"
                               Content="{Binding}"
                               FontFamily="Segoe UI"
                               FontSize="14"
                               Padding="5,2" />
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
            <Button Width="200" Height="30" />
        </StackPanel>
    </Popup>

</StackPanel>

UserControl.xaml.vb

Imports System.ComponentModel
Imports System.Linq.Expressions
Imports System.Collections.ObjectModel

Class UserControl1

' Dependency Properties
Public Shared ReadOnly ListProperty As DependencyProperty = DependencyProperty.Register("List", GetType(ObservableCollection(Of Integer)), GetType(MainWindow))
Public Shared ReadOnly ValueProperty As DependencyProperty = DependencyProperty.Register("Value", GetType(Integer), GetType(MainWindow))
Public Shared ReadOnly MaxValueProperty As DependencyProperty = DependencyProperty.Register("MaxValue", GetType(Integer), GetType(MainWindow))
Public Shared ReadOnly MinValueProperty As DependencyProperty = DependencyProperty.Register("MinValue", GetType(Integer), GetType(MainWindow))

' Properties
Public Property List As ObservableCollection(Of Integer)
    Get
        Return DirectCast(GetValue(ListProperty), ObservableCollection(Of Integer))
    End Get
    Set(value As ObservableCollection(Of Integer))
        SetValue(ListProperty, value)
    End Set
End Property

Public Property Value As Integer
    Get
        Return DirectCast(GetValue(ValueProperty), Integer)
    End Get
    Set(value As Integer)
        SetValue(ValueProperty, value)

    End Set
End Property

Public Property MaxValue As Integer
    Get
        Return DirectCast(GetValue(MaxValueProperty), Integer)
    End Get
    Set(value As Integer)
        SetValue(MaxValueProperty, value)
    End Set
End Property

Public Property MinValue As Integer
    Get
        Return DirectCast(GetValue(MinValueProperty), Integer)
    End Get
    Set(value As Integer)
        SetValue(MinValueProperty, value)
    End Set
End Property

Private Sub ListView_SelectionChanged(sender As System.Object, e As System.Windows.Controls.SelectionChangedEventArgs)
    Value = List(myListView.SelectedIndex)
End Sub

Private Sub UserControl1_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Loaded
    List = New ObservableCollection(Of Integer)

    ' Add all available numbers into the list
    For iCounter As Integer = MinValue To MaxValue
        List.Add(iCounter)
    Next

    ' Set the selected index on the list for the value
    myListView.SelectedIndex = Value - MinValue
End Sub
End Class

仅供参考,当我测试时,我在窗口和用户控件设置中自己设置了最小值和最大值。

3个回答

19

我认为你犯了我刚开始学习WPF时经常犯的错误。我不能保证这是你问题的原因,但因为这是我唯一能看到的问题,所以我会解决它。不幸的是,有太多糟糕的教程和快速示例展示将UserControl.DataContext连接到自身:

DataContext="{Binding RelativeSource={RelativeSource Self}}"

或者:

DataContext = this;

如果你不想在外部与UserControl绑定,那么这是完全可以接受的,因为这是一种快速而简便的方式,可以连接到代码后面定义的属性。然而,当你想从控件外部绑定属性时,你会遇到问题。在这些情况下(如果不是所有情况),你应该使用RelativeSource Binding来绑定到你的代码后面的属性:

<TextBox Name="myTextbox" Height="30" Margin="0" FontSize="14" IsReadOnly="True"
    Padding="5,2" Text="{Binding Value, RelativeSource={RelativeSource AncestorType={
    x:Type UserControl1}}}" />
在这个 Binding 中,UserControl1 是声明属性的UserControl的名称。这应该在所有内部的Binding中完成。这样,DataContext不会设置为UserControl,但是Binding仍然可以找到属性。
你可以从MSDN上的RelativeSource MarkupExtension页面了解更多关于RelativeSource的信息。

9

很抱歉,由于无法对Sheridan的好答案进行评论,我需要提供一份新的答案。

虽然我喜欢这个解决方案

DataContext="{Binding RelativeSource={RelativeSource Self}}"

正如Sheridan已经指出的那样,它会快速失败。

您可以做的就是设置您的用户控件内容的数据上下文。

<UserControl x:Class="Example.View.Controls.MyUserControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:controls="clr-namespace:Example.View.Controls"
         mc:Ignorable="d">
<Grid DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type controls:MyUserControl}}}">

</Grid>

这样一来,所有后续的绑定都会减少样板代码,因为您可以直接从代码后台绑定到您的 DP,例如:

<Label Content="{Binding MyLabel}"/>

谢谢。同样的问题存在,并且这在C#中也可以解决。 - mcy

1
Windows应用程序(Windows 8和Windows 10 UWP)而言,最好的方法是给您的控件命名,并在XAML文件中使用PathElementName进行引用:
<UserControl
x:Class="MyControl"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="Control"
mc:Ignorable="d" >

   <Grid Height="240" VerticalAlignment="Top">
     <Rectangle Fill="{Binding ElementName=Control, Path=Background}" />
   </Grid>
</UserControl>

``


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接