WPF用户控件数据绑定不起作用

时间:2013-10-04 21:37:37

标签: wpf vb.net data-binding user-controls wpf-controls

我正在创建一个简单的用户控件,将弹出窗口与文本视图相结合,没什么了不起的。当我首先在窗口中设置它以将其全部设置出来时,它完美地工作,但是当我将其移动到用户控件中以实际完成它时,它将无法正常工作。

我将最小值和最大值传递给控件,​​然后它会自动创建一个可在该范围内选择的数字列表。在用户控件中,数字列表没有正确绑定,谁知道原因。也许有人可以看看我的代码。

我已经阅读了很多关于此问题的其他问题,但不知道发生了什么。我的输出窗口没有出现任何错误,所以没有线索。无论如何,这是代码 -

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 个答案:

答案 0 :(得分:13)

我认为你在我第一次开始学习WPF时犯了同样的错误。我不能保证这个 是你问题的原因,但因为它是我能看到的唯一一个,我会解决它。不幸的是,有很多糟糕的教程和快速示例显示了UserControl.DataContext与自身的连接:

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

或者:

DataContext = this;

现在,如果想要Bind外部UserControl,这是完全可以接受的,因为这是一种快速简便的方式来连接代码中定义的属性背后。但是,当您想要从控件外部Bind到属性时,您会发现问题。在这些情况下(如果不是在所有情况下),您应该使用RelativeSource Binding Bind到您的代码后面的代码:

<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的更多信息。

答案 1 :(得分:9)

由于我无法向Sheridan提供一个好评,我必须提供一个新答案,对不起。

虽然我喜欢这个解决方案

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

它失败了(正如Sheridan已经指出的那样)。

您可以做的只是设置用户控件内容的DataContext

<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>

通过这种方式,所有后续Bindings都具有较少的Boilerplate代码,因为您可以直接从代码隐藏绑定到DP,如:

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

答案 2 :(得分:1)

对于 Windows Apps(Windows 8和Windows 10 UWP),可以使用路径为您的控件命名并在XAML文件中引用它的的ElementName

 function isinValueGetterBox(params) {
    if (params.node.group) { return null; }
    var isinValueBox = "";

    for (var i = 0; i < params.data.security.identifiers.length; i++) {
        if (params.data.security.identifiers[i].type === "isin") {
            isinValueBox = ('<a href = "#" onclick = popup1()>' + params.data.security.identifiers[i].value + '</a>');
        }
    }
    return isinValueBox;
}

``