簡體   English   中英

將集合綁定到自定義控件屬性

[英]Binding a collection to a custom control property

我沒有運氣嘗試將數據集合綁定到我的自定義控件的屬性。 我已經實現了這個控件的字符串屬性的機制(從這里得到了一些幫助),並希望集合類型也能如此簡單。 但是我不能讓它再次工作。

這是我的自定義控件視圖

<UserControl x:Class="BadaniaOperacyjne.Controls.Matrix"
            mc:Ignorable="d" Name="CustomMatrix"
            d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Grid.RowDefinitions>
            <!-- ... -->
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <!-- ... -->
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding ElementName=CustomMatrix, Path=Title}"/>
        <Grid Grid.Row="2" Grid.Column="1" Name="contentGrid">
            <ListBox ItemsSource="{Binding ElementName=CustomMatrix, Path=ItemsList}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding}"/>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </Grid>
</UserControl>

及其代碼隱藏

#region ItemsList Property
public static readonly DependencyProperty ItemsListProperty =
    DependencyProperty.Register("ItemsList", typeof(ObservableCollection<object>), typeof(Matrix), new PropertyMetadata(new ObservableCollection<object>(), new PropertyChangedCallback(ItemsListChanged)));
public ObservableCollection<object> ItemsList
{
    get { return GetValue(ItemsListProperty) as ObservableCollection<object>; }
    set { SetValue(ItemsListProperty, value); }
}
private void ItemsListChanged(object value)
{
    System.Diagnostics.Debug.WriteLine("matrix: items list changed " + value);
    if (ItemsList != null)
    {
        ItemsList.CollectionChanged += ItemsList_CollectionChanged;
        System.Diagnostics.Debug.WriteLine("got " + string.Join(",", ItemsList.ToList()));
    }
    else
    {
        System.Diagnostics.Debug.WriteLine("got null");
    }
}

void ItemsList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
    System.Diagnostics.Debug.WriteLine("matrix: current items list collection changed");
}
private static void ItemsListChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    // a breakpoint
    ((Matrix)d).ItemsListChanged(e.NewValue);
}
#endregion

// showing the Title property implementation just to state that
// it is done the same way as for ItemsList
#region Title Property
public static readonly DependencyProperty TitleProperty = 
    DependencyProperty.Register("Title", typeof(string), typeof(Matrix), new PropertyMetadata("", new PropertyChangedCallback(TitleChanged)));
public string Title
{
    get { return (string)GetValue(TitleProperty); }
    set { SetValue(TitleProperty, value); }
}
private void TitleChanged(string title)
{
    System.Diagnostics.Debug.WriteLine("matrix: title changed to: " + title);
}
private static void TitleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    ((Matrix)d).TitleChanged((string)e.NewValue);
}
#endregion

這就是我試圖綁定到該控件的方式

<custom:Matrix x:Name="customMatrix" DockPanel.Dock="Top" Title="{Binding Title}" ItemsList="{Binding Items}"/>

主頁的代碼隱藏是

//internal ObservableCollection<List<int>> ItemsList { get; set; }
public class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public ViewModel()
    {
        Items = new ObservableCollection<int> { 1, 2, 3, 4, 5, 6};
    }

    void Items_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        System.Diagnostics.Debug.WriteLine("problem manager: items list changed " + e.NewItems.Count);
    }

    public ObservableCollection<int> Items { get; set; }

    protected string title;
    public string Title
    {
        get { return title; }
        set
        {
            if (title != value)
            {
                title = value;
                NotifyPropertyChanged("Title");
            }
        }
    }

    protected void NotifyPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
}

public ViewModel VM { get; private set; }

// this is the window constructor
private ProblemManager() 
{
    VM = new ViewModel();

    DataContext = VM;
    InitializeComponent();

    VM.Title = "title";
}

private int i = 0;
private void btnAddRow_Click(object sender, RoutedEventArgs e)
{
    // when doing either of these two lines below, 
    // the control breakpoint is never hit
    VM.Items.Add(++i);
    VM.Items = new ObservableCollection<int> { 2, 3 };

    // however, when directly assigning to the control's property, 
    // the event is raised and the breakpoint is hit and the UI is updated
    customMatrix.ItemsList = new ObservableCollection<object> { 1, 2, 3 };
    customMatrix.ItemsList.Add(66);
    // and this of course makes the control's title update
    VM.Title = (++i).ToString();
}

我相信,控件的TitleItemsList DependencyProperty都是以相同的方式創建的。 盡管如此,綁定可能無法正常工作,因為該綁定不會引發ItemsListChanged事件。 因此,問題是我無法通過 XAML 將窗口的ViewModel.Items集合綁定到控件的ItemsList集合。 正在創建一個DependencyProperty控制之內的收集來自任何不同DependencyProperty一個簡單的string屬性?

問題在於您的DependencyProperty注冊。 Co-variance is not applicable for generic lists即您不能這樣做 -

ObservableCollection<object> objects = new ObservableCollection<int>();

您已將 DP 類型聲明為ObservableCollection<object>但將其與ObservableCollection<int>類型的列表綁定。

您應該change either type of DP to ObservableCollection<int>change binding collection type to ObservableCollection<object>

public ViewModel()
{
    Items = new ObservableCollection<object> { 1, 2, 3, 4, 5, 6};
}

public ObservableCollection<object> Items { get; set; }

經過這么長時間,我有點需要它以兩種方式工作:

  1. 能夠定義任何類型的項目的集合
  2. 在添加/刪除項目等時收到通知。

為了更精確,我正在研究像控制一樣的面包屑。

實際上,ItemsSource DP 是用 IList 類型定義的,以允許上述“通用性”:

public static readonly DependencyProperty ItemsSourceProperty =
  DependencyProperty.Register("ItemsSource", typeof(IList), typeof(BreadCrumbUserControl), 
    new PropertyMetadata(null, new PropertyChangedCallback(OnItemsSourceChanged)));

這是我如何使用我的面包屑:

<views:BreadCrumbUserControl ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}"/>

其中 Items 是自定義類型的 ObservableCollection:

public ObservableCollection<BaseItem> Items { get; set; }

實際上它工作正常,我的 ItemsSource 本地屬性指向正確的集合,但是當一個項目添加到 ObservableCollection 時,我需要在我的 UserControl 中得到通知。

所以,我在我的 ItemsSource DP 上使用了 PropertyChangedCallback 委托。

private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
  BreadCrumbUserControl thisUserControl = (BreadCrumbUserControl)d;
  (e.NewValue as INotifyCollectionChanged).CollectionChanged += thisUserControl.BreadCrumbUserControl_CollectionChanged;
}

這是對我有用的技巧,因為我的輸入引用實際上是在上層定義的 ObservableCollection。

現在這兩個目標都已實現:處理任何自定義類型和相同類型的集合,隨時了解情況。

如果設置了協議,反射可用於更改我們自定義類型的值。

這只是一個迭代我的集合的例子,它在我的 UserControl 的世界中有一些未知的類型:

foreach (var baseItem in ItemsSource)
{
   baseItem.GetType().GetProperty("IsActive").SetValue(baseItem, false);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM