【问题标题】:Pass ObservableCollection<> type as dependency property将 ObservableCollection<> 类型作为依赖属性传递
【发布时间】:2020-09-14 12:43:59
【问题描述】:

我正在尝试创建一个多选组合框自定义控件,该自定义控件应该公开一个名为 DropDownDataSource 的依赖属性,控件的用户可以通过该属性决定哪一天应该绑定到 ComboBox。我的代码如下所示:

MainPage.Xaml

<Grid>
    <local:CustomComboBox x:Name="customcb" DropDownDataSource="{x:Bind DropDownDataSource, Mode=OneWay}"  Loaded="CustomControl_Loaded"> </local:CustomComboBox>
</Grid>

MainPage.Xaml.cs

public sealed partial class MainPage : Page, INotifyPropertyChanged
{
    private ObservableCollection<Item> _dropDownDataSource;
    public ObservableCollection<Item> DropDownDataSource
    {
        get => _dropDownDataSource;
        set
        {
            _dropDownDataSource = value;
            OnPropertyChanged();
        }
    }

    public MainPage()
    {
        this.InitializeComponent();
    }

    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged([CallerMemberName]string name = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
    private void CustomControl_Loaded(object sender, RoutedEventArgs e)
    {

       
        var Items = new ObservableCollection<Item>(Enumerable.Range(1, 10)
              .Select(x => new Item
              {
                  Text = string.Format("Item {0}", x),
                  IsChecked = x == 40 ? true : false
              }));
        DropDownDataSource = Items;
    
    }
}

型号

    public class Item : BindableBase
{
    public string Text { get; set; }

    bool _IsChecked = default;
    public bool IsChecked { get { return _IsChecked; } set { SetProperty(ref _IsChecked, value); } }
}

public abstract class BindableBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected void SetProperty<T>(ref T storage, T value,
        [System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
    {
        if (!object.Equals(storage, value))
        {
            storage = value;
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    protected void RaisePropertyChanged([System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

自定义用户控件 XAML

  <Grid x:Name="GrdMainContainer">
   
    <StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center">
        <TextBox Width="200" FontSize="24" Text="{Binding Header, Mode=TwoWay}" 
             IsReadOnly="True" TextWrapping="Wrap" MaxHeight="200" />
        <ScrollViewer VerticalScrollBarVisibility="Auto" MaxHeight="200" Width="200" Background="White">
            <ItemsControl ItemsSource="{Binding Items}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <CheckBox Content="{Binding Text}" 
                      FontSize="24" 
                      Foreground="Black"
                      IsChecked="{Binding IsChecked, Mode=TwoWay}" 
                      IsThreeState="False" />
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </ScrollViewer>
    </StackPanel>
</Grid>

CustomUserControl Cs 文件

    public sealed partial class CustomComboBox : UserControl
{
    public CustomComboBox()
    {
        this.InitializeComponent();

    }
    public ObservableCollection<Item> DropDownDataSource
    {
        get { return (ObservableCollection<Item>)GetValue(DropDownDataSourceProperty); }
        set { SetValue(DropDownDataSourceProperty, value); }
    }

    public static readonly DependencyProperty DropDownDataSourceProperty =
        DependencyProperty.Register("DropDownDataSource", typeof(ObservableCollection<Item>), typeof(CustomComboBox), new PropertyMetadata("", HasDropDownItemUpdated));
    private static void HasDropDownItemUpdated(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is CustomComboBox ucrcntrl)
        {
            var grd = UIElementExtensions.FindControl<Grid>(ucrcntrl, "GrdMainContainer");
            grd.DataContext = ucrcntrl.DropDownDataSource as ObservableCollection<Item>;
        }
    }

}

在我看来一切都很好,但由于某种原因,Dropdown 是空的。如果我将视图模型直接分配给控件,而不是依赖属性,则它可以正常工作。但在我的情况下,我必须在用户控件上具有 DataSource、SelectedIndex 等属性以供最终用户使用。谁能指出这里出了什么问题? Here,我附上了我的完整代码的副本。

【问题讨论】:

  • file.io URL 在获取 404 时似乎不正确
  • 为什么要使用自定义组合框来扩展用户控件?你不能简单地扩展现有的组合框类并使用标准的 itemssource 吗?或者,如果您只需要更改它的外观,只需创建一个新的控件模板
  • @the.Doc 我正在尝试创建一个多选组合框,它在 UWP 中不可用

标签: c# xaml uwp uwp-xaml


【解决方案1】:

我下载了你的示例代码,问题应该出在绑定上。

<ItemsControl ItemsSource="{Binding Items}">

不推荐这种写法。在ObservableCollection 中,Items 是受保护的属性,不适合作为绑定属性。

你可以尝试直接在ItemsControl中绑定依赖属性:

<ItemsControl ItemsSource="{x:Bind DropDownDataSource,Mode=OneWay}">
    <ItemsControl.ItemTemplate>
        <DataTemplate x:DataType="local:Item">
            <CheckBox IsChecked="{x:Bind IsChecked, Mode=TwoWay}" 
                      IsThreeState="False" >
                <TextBlock Text="{x:Bind Text}" Foreground="Black" FontSize="24"/>
            </CheckBox>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

另外大家可能注意到了,我修改了CheckBox的样式,将内容改写为TextBlock,因为在CheckBox的默认样式中,Foreground并没有绑定到内部的ContentPresenter .

谢谢。

【讨论】:

    猜你喜欢
    • 2016-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-20
    • 2023-04-04
    • 2011-08-15
    • 1970-01-01
    • 2014-03-01
    相关资源
    最近更新 更多