【问题标题】:Order List by Property Name按属性名称排序列表
【发布时间】:2020-02-08 23:08:00
【问题描述】:

我编写了一个函数来在单击列标题时对 ListView 进行排序。我现在希望该功能适用​​于任何列。我的第一个想法是传递一个表示列中显示的属性的字符串,并使用ifswitch 按正确的属性进行排序,例如:

private bool sortOrder = false;
public void SortCol(string property)
{
    OUModel ou = ADRoot.Descendants().Where(node => node.IsSelected == true).FirstOrDefault() as OUModel;

    if (ou != null)
    {
        switch (property)
        {
            case "Name":
                if (sortOrder)
                {
                    ou.Computers = new List<RemoteComputer>(ou.Computers.OrderBy(c => c.Name));
                }
                else
                {
                    ou.Computers = new List<RemoteComputer>(ou.Computers.OrderByDescending(c => c.Name));
                }
                break;

            case "LastUpdated":
                if (sortOrder)
                {
                    ou.Computers = new List<RemoteComputer>(ou.Computers.OrderBy(c => c.LastUpdated));
                }
                else
                {
                    ou.Computers = new List<RemoteComputer>(ou.Computers.OrderByDescending(c => c.LastUpdated));
                }
                break;
                // etc...
        }
        sortOrder = !sortOrder;
    }
}

但这显然会变得相当冗长和重复。相反,我想知道我是否可以通过财产本身。但我试图从 XAML 传递属性类型(使用 Caliburn.Micro Actions),但不知道如何。

private bool sortOrder = false;
public void SortCol<T>(Func<RemoteComputer, T> property)
{
    OUModel ou = ADRoot.Descendants().Where(node => node.IsSelected == true).FirstOrDefault() as OUModel;

    if (ou != null)
    {
        if (sortOrder)
        {
            ou.Computers = new List<RemoteComputer>(ou.Computers.OrderBy(property));
        }
        else
        {
            ou.Computers = new List<RemoteComputer>(ou.Computers.OrderByDescending(property));
        }
        sortOrder = !sortOrder;
    }
}

XAML:

    <!-- Name Spaces
    xmlns:models="clr-namespace:MyProject.Models"
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    xmlns:cal="http://www.caliburnproject.org"
    -->

    <!-- TreeView the ListView is bound to -->
    <TreeView x:Name="ADTree" ItemsSource="{Binding ADRoot.Children}">
    <!-- -->

    <ListView x:Name="ADComputers" ItemsSource="{Binding SelectedItem.Computers, ElementName=ADTree, Mode=TwoWay}" SelectionMode="Multiple" ItemContainerStyle="{DynamicResource RemoteComputerItem}">
        <ListView.View>
            <GridView>
                <GridViewColumn x:Name="Name" Width="120" DisplayMemberBinding="{Binding Name}">
                    <GridViewColumn.Header>
                        <GridViewColumnHeader x:Name="SortName" Content="Computer Name">
                            <i:Interaction.Triggers>
                                <i:EventTrigger EventName="Click">
                                    <cal:ActionMessage MethodName="SortCol">
     <!-- Trying to pass type here, but nested types are not supported. Don't know how else I can do this -->
                                        <cal:Parameter Value="{x:Type models:RemoteComputer.Name}" />
                                    </cal:ActionMessage>
                                </i:EventTrigger>
                            </i:Interaction.Triggers>
                        </GridViewColumnHeader>
                    </GridViewColumn.Header>
                </GridViewColumn>
                <!-- Other Columns -->
            </GridView>
        </ListView.View>
    </ListView>

类等:

class ADTreeViewModel : OUModel
{    
    public OUModel ADRoot { get; set; } = new OUModel(null, false); // <<< TreeView is bound to this, and ListView is bound to TreeView.SelectedItem.Computers
}

class OUModel : PropertyChangedBase, ITreeViewItemViewModel
{
    private List<RemoteComputer> computers = new List<RemoteComputer>();
    public List<RemoteComputer> Computers { get { return computers; } set { computers = value; NotifyOfPropertyChange(); } }
}

class RemoteComputer : PropertyChangedBase
{
    public string Name
    {
        get { return name; }
        set { name = value; NotifyOfPropertyChange(() => Name); }
    }

    // Shortening the other properties, but you get the idea...
    public IPAddress IP { //etc }
    public ComputerStatus Status { //etc }
    public DateTime LastUpdated { //etc }

    // and so on...
}

【问题讨论】:

    标签: c# wpf data-binding caliburn.micro


    【解决方案1】:

    您不需要开关。如果您想按名称访问您的属性,您可以创建一个Expression 来构建一个Func

    private bool sortOrder = false;
    public void SortCol(string propName)
    {
        OUModel ou = ADRoot.Descendants().Where(node => node.IsSelected == true).FirstOrDefault() as OUModel;
    
        if (ou != null)
        {
            if (sortOrder)
                ou.Computers = ou.Computers.OrderBy(GetProperty(propName)).ToList();
            else
                ou.Computers = ou.Computers.OrderByDescending(GetProperty(propName)).ToList();
            sortOrder = !sortOrder;
        }
    }
    
    // this method creates an Expression<Func<RemoteComputer,object>>
    // and returning its compiled() resutl. Func<RemoteComputer,object>
    // so you can use this `Func` in your orderBy methods.
    public Func<T, object> GetProperty<T>(string propertyName)
    {
    // q =>
        ParameterExpression param = Expression.Parameter(typeof(T), "q");
    // q.propertyName  
    // for example : q.Name
        MemberExpression member = Expression.PropertyOrField(param, propertyName);
    // change property type to object (because we want to use all posible types of our class peroperties or fields)
        var body = Expression.Convert(member, typeof(object));
    // create Expression<Func<RemoteComputer,object>>
        var property = Expression.Lambda<Func<T, object>>(body, param);
    // returns a Func<T,object>
        return property.Compile();
    }
    


    更新

    此外,您可以使用此扩展类将 OrderBy 字符串支持添加到整个应用程序中的列表中。这也适用于 IQueryable 查询。

    public static class OrderByExtentions
    {
        public static IOrderedEnumerable<T> OrderByDescending<T>(this IEnumerable<T> list, string propertyName )
        {
            return list.OrderByDescending(GetPropertyFunc<T>(propertyName));
        }
        public static IQueryable<T> OrderByDescending<T>(this IQueryable<T> list, string propertyName)
        {
            return list.OrderByDescending(GetPropertyExpression<T>(propertyName));
        }
        public static IOrderedEnumerable<T> OrderBy<T>(this IEnumerable<T> list, string propertyName)
        {
            return list.OrderBy(GetPropertyFunc<T>(propertyName));
        }
        public static IQueryable<T> OrderBy<T>(this IQueryable<T> list, string propertyName)
        {
            return list.OrderBy(GetPropertyExpression<T>(propertyName));
        }
        private static Expression<Func<T, object>> GetPropertyExpression<T>(string propertyName)
        {
            ParameterExpression param = Expression.Parameter(typeof(T), "q");
            MemberExpression member = Expression.PropertyOrField(param, propertyName);
            var body = Expression.Convert(member, typeof(object));
            return Expression.Lambda<Func<T, object>>(body, param);
        }
        private static Func<T, object> GetPropertyFunc<T>(string propertyName)
        {
            return GetPropertyExpression<T>(propertyName).Compile();
        }
    }
    
    

    用法:

    // eg. propName = "Name";
    
    ou.Computers = ou.Computers.OrderByDescending(propName).ToList();
    
    // or
    
    ou.Computers = ou.Computers.OrderBy(propName).ToList();
    

    【讨论】:

    • 效果很好,谢谢。您介意稍微解释一下GetProperty 方法吗?
    • @Daniel 我在每一步都添加了一些 cmets。希望对你有帮助
    • @Daniel 如果您不介意,请将您的问题标题更改为 OrderBy list by property name 这样您就可以帮助其他遇到类似问题的人。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-23
    • 1970-01-01
    • 2017-06-20
    • 1970-01-01
    相关资源
    最近更新 更多