简体   繁体   English

将控件的 ItemsSource 作为值传递给依赖项属性

[英]Pass a control's ItemsSource as a value to a dependency property

I have a custom ListView Control that takes a list of ModelItems as its ItemsSource:我有一个自定义 ListView 控件,它将 ModelItems 列表作为其 ItemsSource:

          <customControls:ListViewEx Name="DocumentListView"
                ItemsSource="{Binding ModelItems, Mode=OneWay}">

I also have a dependency property GridViewColumnHeaderClick.HeaderClick , the property type of which is object :我还有一个依赖属性GridViewColumnHeaderClick.HeaderClick ,其属性类型是object

        public static readonly DependencyProperty HeaderClickProperty = DependencyProperty.RegisterAttached(
        "HeaderClick", 
        typeof(object), 
        typeof(GridViewColumnHeaderClick), 
        new UIPropertyMetadata(null, OnHeaderClickChanged));

I would like ModelItems to be passed as the value to this dependency property, and I've attempted to do as so:我希望将ModelItems作为值传递给此依赖项属性,并且我尝试这样做:

            <GridView.ColumnHeaderContainerStyle>
              <Style BasedOn="{StaticResource {x:Type GridViewColumnHeader}}" TargetType="{x:Type GridViewColumnHeader}">
                <Setter Property = "misc:GridViewColumnHeaderClick.HeaderClick" 
                        Value="{Binding ModelItems}"/>
              </Style>
            </GridView.ColumnHeaderContainerStyle>

and in C#:在 C# 中:

            ICollectionView dataView =
            CollectionViewSource.GetDefaultView(HeaderClickProperty);

However, nothing seems to happen when I click the header.但是,当我单击 header 时,似乎什么也没发生。 I'm not sure if it's because I'm binding it incorrectly in the XAML, or something completely different.我不确定是不是因为我在 XAML 中错误地绑定了它,还是完全不同的东西。

EDIT: apologies for the lack of details, I understand this is a bit messy.编辑:为缺乏细节道歉,我知道这有点混乱。

My overall aim is to run a sort function by clicking on header of the GridViewColumn.我的总体目标是通过单击 GridViewColumn 的 header 来运行排序 function。 The GridView is a child of ListViewEx . GridViewListViewEx的子级。 When the header is clicked, I wish to bind it to the HeaderClick property and set the value as the ItemsSource of the ListView (in this case ItemsSource=ModelItems ) so I can sort it.单击 header 时,我希望将其绑定到HeaderClick属性并将值设置为ListViewItemsSource (在本例中ItemsSource=ModelItems ),以便对其进行排序。

When clicked the function OnHeaderClickChanged is run:单击 function OnHeaderClickChanged时运行:

        private static void OnHeaderClickChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        var headerClicked = sender as GridViewColumnHeader;
        headerClicked.MouseUp += (s, me) => HeaderClickedOnMouseUp(s, me, e.NewValue);
    }

This adds the function HeaderClickedOnMouseUp to the MouseUp event.这会将 function HeaderClickedOnMouseUp添加到MouseUp事件。 This function is the one carries out the sorting with the ModelItems .这个 function 是用ModelItems进行排序的。

My issue is my lack of understanding when it comes to dependency/attached properties and how to bind it with the view.我的问题是我对依赖项/附加属性以及如何将其与视图绑定缺乏了解。 As correctly mentioned in the comments, the setter is not called at any point when I try debugging it and I'm lost as to why this is the case.正如评论中正确提到的那样,当我尝试调试它时,在任何时候都不会调用 setter,我不知道为什么会这样。

This is a simple sorting example which uses ascending sorting.这是一个使用升序排序的简单排序示例。 The Attached Property Sort.IsEnabled should be set on a GridViewColumnHeader .附加属性Sort.IsEnabled应设置在GridViewColumnHeader上。 The sorting itself is done by setting the SortDescription of the default CollectionView of the column's parent ListView.ItemsSource .排序本身是通过设置列的父ListView.ItemsSource的默认CollectionViewSortDescription来完成的。

Recommended read:推荐阅读:
Attached Properties Overview 附加属性概述
Dependency properties overview 依赖属性概述

Data model: Person.cs数据 model: Person.cs

class Person
{
  public Person(string firstName, string lastName)
  {
    this.FirstName = firstName;
    this.LastName = lastName;
  }

  public string FirstName { get; set; }
  public string LastName { get; set; }
}

ViewModel.cs视图模型.cs

class ViewModel : INotifyPropertyChanged
{
  public ViewModel()
  {
    this.Persons = new ObservableCollection<Person>()
    {
      new Person("Derek", "Zoolander"),
      new Person("Tony", "Montana"),
      new Person("The", "Dude")
    };
  }

  public ObservableCollection<Person> Persons { get; set; }
}   

The Attached property Sort : Sort.cs附加属性SortSort.cs

class Sort : DependencyObject
{
  #region IsEnabled attached property

  public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached(
    "IsEnabled", typeof(bool), typeof(Sort), new PropertyMetadata(false, Sort.OnIsEnabled));

  public static void SetIsEnabled([NotNull] DependencyObject attachingElement, bool value) => attachingElement.SetValue(Sort.IsEnabledProperty, value);

  public static bool GetIsEnabled([NotNull] DependencyObject attachingElement) => (bool) attachingElement.GetValue(Sort.IsEnabledProperty);

  #endregion

  private static void OnIsEnabled(DependencyObject attachingElement, DependencyPropertyChangedEventArgs e)
  {
    bool isSortingEnabled = (bool) e.NewValue;
    if (isSortingEnabled == (bool) e.OldValue)
    {
      return;
    }

    if (attachingElement is GridViewColumnHeader columnHeader)
    {
      if (isSortingEnabled)
      {
        columnHeader.Click += Sort.SortByColumn;
      }
      else
      {
        columnHeader.Click -= Sort.SortByColumn;
      }  
    }
  }

  private static void SortByColumn(object sender, RoutedEventArgs e)
  {
    var columnHeader = sender as GridViewColumnHeader;
    PropertyPath columnSourceProperty = (columnHeader.Column.DisplayMemberBinding as Binding).Path;

    // Use an extension method to find the parent ListView 
    // by traversing the visual tree
    if (columnHeader.TryFindVisualParentElement(out ListView parentListView))
    {
      var collectionView = (CollectionView)CollectionViewSource.GetDefaultView(parentListView.ItemsSource);
      collectionView.SortDescriptions.Clear();

      // Apply sorting
      collectionView.SortDescriptions.Add(new SortDescription(columnSourceProperty.Path, ListSortDirection.Ascending));
    }
  }
}

Extension helper method to find a visual parent: Extensions.cs查找可视父级的扩展辅助方法: Extensions.cs

public static class Extensions
{
  /// <summary>
  /// Traverses the visual tree towards the root until an element with a matching element name is found.
  /// </summary>
  /// <typeparam name="TParent">The type the visual parent must match.</typeparam>
  /// <param name="child"></param>
  /// <param name="resultElement"></param>
  /// <returns><c>true</c> when the parent visual was found otherwise <c>false</c></returns>
  public static bool TryFindVisualParentElement<TParent>(this DependencyObject child, out TParent resultElement)
    where TParent : DependencyObject
  {
    resultElement = null;

    DependencyObject parentElement = VisualTreeHelper.GetParent(child);

    if (parentElement is TParent parent)
    {
      resultElement = parent;
      return true;
    }

    return parentElement?.TryFindVisualParentElement(out resultElement) ?? false;
  }
}

Usage: MainWindow.xaml用法: MainWindow.xaml

<Window>
  <Window.DataContext>
    <ViewModel />
  </Window.DataContext>

  <ListView ItemsSource="{Binding Persons}">
    <ListView.View>
      <GridView>
        <GridViewColumn DisplayMemberBinding="{Binding FirstName}">
          <GridViewColumnHeader Sort.IsEnabled="True" Content="First Name" />
        </GridViewColumn>
        <GridViewColumn DisplayMemberBinding="{Binding LastName}">
          <GridViewColumnHeader Sort.IsEnabled="True" Content="Last Name" />
        </GridViewColumn>
      </GridView>
    </ListView.View>
  </ListView>
</Window>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM