在WPF中的UserControl中的ItemsControl中绑定问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在WPF中的UserControl中的ItemsControl中绑定问题相关的知识,希望对你有一定的参考价值。

我创建了一个简单的示例,它通过更大的用户控件 - 应用程序交互来复制我遇到的问题。控件已更改为简化,但反映了确切的问题。

我有一个用户控件(CheckBoxTable),它根据属性CheckBoxData创建一个复选框网格:

<UserControl
    x:Class="WPFNotWorkingTest.CheckBoxTable"
    x:Name="CheckBoxTableName">
    <ItemsControl DataContext="{Binding ElementName=CheckBoxTableName}" ItemsSource="{Binding CheckBoxData}">
        <ItemsControl.ItemContainerStyle>
            <Style>
                <Setter Property="Grid.Row" Value="{Binding GridRow}" />
                <Setter Property="Grid.Column" Value="{Binding GridColumn}" />
            </Style>
        </ItemsControl.ItemContainerStyle>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Grid ShowGridLines="True" Style="{Binding Path=Style}" Loaded="OnGrid_Loaded"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <CheckBox IsChecked="{Binding IsCheckBoxChecked}"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</UserControl>

ItemSource CheckBoxData是CheckBoxTable用户控件中的ObservableCollection。

public ObservableCollection<CheckBoxTableData> CheckBoxData
{
    get { return (ObservableCollection<CheckBoxTableData>)GetValue(CheckBoxDataProperty); }
    set { SetValue(CheckBoxDataProperty, value); }
}

// Using a DependencyProperty as the backing store for CheckBoxData.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty CheckBoxDataProperty =
     DependencyProperty.Register("CheckBoxData", typeof(ObservableCollection<CheckBoxTableData>), typeof(CheckBoxTable), new PropertyMetadata(null, new PropertyChangedCallback(CheckBoxTable.OnCheckBoxData_Changed)));

private static void OnCheckBoxData_Changed(DependencyObject dObject, DependencyPropertyChangedEventArgs e)
{
    CheckBoxTable table = (CheckBoxTable)dObject;
    ObservableCollection<CheckBoxTableData> objOldValue = (ObservableCollection<CheckBoxTableData>)e.OldValue;
    if (objOldValue != null)
    {
        objOldValue.CollectionChanged -= table.OnTableData_CollectionChanged;
    }

    ObservableCollection<CheckBoxTableData> objNewValue = (ObservableCollection<CheckBoxTableData>)e.NewValue;
    if (objNewValue != null)
    {
        objNewValue.CollectionChanged += table.OnTableData_CollectionChanged;
    }
}

CheckBoxTableData类

    public class CheckBoxTableData : DependencyObject
    {
        public bool? IsCheckBoxChecked
        {
            get { return (bool?)GetValue(IsCheckBoxCheckedProperty); }
            set { SetValue(IsCheckBoxCheckedProperty, value); }
        }
        // Using a DependencyProperty as the backing store for IsCheckBoxChecked.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsCheckBoxCheckedProperty =
             DependencyProperty.Register("IsCheckBoxChecked", typeof(bool?), typeof(CheckBoxTableData), new PropertyMetadata(true));

        public int GridRow
        {
            get { return (int)GetValue(GridRowProperty); }
            set { SetValue(GridRowProperty, value); }
        }

        // Using a DependencyProperty as the backing store for GridRow.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty GridRowProperty =
             DependencyProperty.Register("GridRow", typeof(int), typeof(CheckBoxTableData), new PropertyMetadata(0));

        public int GridColumn
        {
            get { return (int)GetValue(GridColumnProperty); }
            set { SetValue(GridColumnProperty, value); }
        }

        // Using a DependencyProperty as the backing store for GridColumn.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty GridColumnProperty =
             DependencyProperty.Register("GridColumn", typeof(int), typeof(CheckBoxTableData), new PropertyMetadata(0));
    }
}

在窗口中使用:

<Window x:Class="WPFNotWorkingTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WPFNotWorkingTest"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="50"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Button Content="Click Me" Click="Button_Click" Margin="5"/>
        <local:CheckBoxTable Grid.Row="1" CheckBoxPerRow="10">
            <local:CheckBoxTable.CheckBoxData>
                <local:CheckBoxTableData IsCheckBoxChecked="True"/>
                <local:CheckBoxTableData IsCheckBoxChecked="False"/>
                <local:CheckBoxTableData IsCheckBoxChecked="{Binding CheckMyBinding}"/>
            </local:CheckBoxTable.CheckBoxData>
        </local:CheckBoxTable>
    </Grid>
</Window>

MainWindow.cs

public partial class MainWindow : Window
{
    public bool? CheckMyBinding
    {
        get { return (bool?)GetValue(CheckMyBindingProperty); }
        set { SetValue(CheckMyBindingProperty, value); }
    }

    // Using a DependencyProperty as the backing store for CheckMyBinding.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty CheckMyBindingProperty =
         DependencyProperty.Register("CheckMyBinding", typeof(bool?), typeof(MainWindow), new PropertyMetadata(false));


    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        if (CheckMyBinding == true)
        {
            CheckMyBinding = false;
        }
        else
        {
            CheckMyBinding = true;
        }
    }
}

单击按钮并执行处理程序不会切换CheckBox的IsChecked。问题似乎是窗口中的绑定而不是用户控件,我已经破坏了我的大脑试图找出原因。

答案

发生这种情况是因为你的CheckBoxTableData无法知道你的MainWindow。由CheckBoxTable中的ItemsControl生成的CheckBoxes都有一个CheckBoxData实例作为它们的DataSource。然而,CheckBoxTableData没有自己的DataContext,你不能使用RelativeSource,因为它们不是应用程序的可视或逻辑树的一部分。

XAML设计师可能会使它看起来像它的工作(显示自动完成和所有),但这只是因为它不知道你的控件是如何工作的。

您可以通过在代码中手动设置此绑定来解决此问题,如下所示:

CheckBoxTableData tableData = new CheckBoxTableData();
BindingOperations.SetBinding(tableData, CheckBoxTableData.IsCheckBoxCheckedProperty,
    new Binding("CheckMyBinding") {Source = this}); // this = MainWindow

checkBoxTable.CheckBoxData = new ObservableCollection<CheckBoxTableData>();
checkBoxTable.CheckBoxData.Add(tableData);

Button toggling CheckBox

但是,在ViewModel中处理此问题而不是将该属性添加到MainWindow会更加清晰。

以上是关于在WPF中的UserControl中的ItemsControl中绑定问题的主要内容,如果未能解决你的问题,请参考以下文章

在WPF中的UserControl中的ItemsControl中绑定问题

如何从 UserControl 访问 WPF 页面中的公共变量?

无法在WPF中创建继承的usercontrol,本地命名空间中的基本控件“不存在”

从 Catel WPF UserControl 中的 ResourceDictionary 中绑定

如何使 WPF UserControl 中的一个 DependencyProperty 更改另一个 [重复]

如何绑定到WPF中的usercontrol内的控件?