【问题标题】:WPF MVVM: how to bind GridViewColumn to ViewModel-Collection?WPF MVVM:如何将 GridViewColumn 绑定到 ViewModel-Collection?
【发布时间】:2011-02-08 06:28:07
【问题描述】:

在我的视图中,我有一个 ListView 绑定到我的 ViewModel 中的 CollectionView,例如:

<ListView ItemsSource="{Binding MyCollection}" IsSynchronizedWithCurrentItem="true">
  <ListView.View>
    <GridView>
      <GridViewColumn Header="Title" DisplayMemberBinding="{Binding Path=Title}"/>
      <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Path=Name}"/>
      <GridViewColumn Header="Phone" DisplayMemberBinding="{Binding Path=Phone}"/>
      <GridViewColumn Header="E-mail" DisplayMemberBinding="{Binding Path=EMail}"/>
    </GridView>
  </ListView.View>
</ListView>

现在这些 GridViewColumns 是固定的,但我希望能够从 ViewModel 更改它们。我想我必须将 GridViewColumn-collection 绑定到 ViewModel 中的某些东西,但是什么,以及如何?
ViewModel 对 WPF 一无所知,所以我不知道如何在 MVVM 中实现这一点。

这里有什么帮助吗?

【问题讨论】:

    标签: c# wpf mvvm binding


    【解决方案1】:

    Columns 属性不是依赖属性,所以不能绑定它。但是,可以创建一个附加属性,您可以将其绑定到 ViewModel 中的集合。然后,此附加属性将为您创建列。


    更新

    好的,这是一个基本的实现......

    附加属性

    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.Reflection;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    
    namespace TestPadWPF
    {
        public static class GridViewColumns
        {
            [AttachedPropertyBrowsableForType(typeof(GridView))]
            public static object GetColumnsSource(DependencyObject obj)
            {
                return (object)obj.GetValue(ColumnsSourceProperty);
            }
    
            public static void SetColumnsSource(DependencyObject obj, object value)
            {
                obj.SetValue(ColumnsSourceProperty, value);
            }
    
            // Using a DependencyProperty as the backing store for ColumnsSource.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty ColumnsSourceProperty =
                DependencyProperty.RegisterAttached(
                    "ColumnsSource",
                    typeof(object),
                    typeof(GridViewColumns),
                    new UIPropertyMetadata(
                        null,
                        ColumnsSourceChanged));
    
    
            [AttachedPropertyBrowsableForType(typeof(GridView))]
            public static string GetHeaderTextMember(DependencyObject obj)
            {
                return (string)obj.GetValue(HeaderTextMemberProperty);
            }
    
            public static void SetHeaderTextMember(DependencyObject obj, string value)
            {
                obj.SetValue(HeaderTextMemberProperty, value);
            }
    
            // Using a DependencyProperty as the backing store for HeaderTextMember.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty HeaderTextMemberProperty =
                DependencyProperty.RegisterAttached("HeaderTextMember", typeof(string), typeof(GridViewColumns), new UIPropertyMetadata(null));
    
    
            [AttachedPropertyBrowsableForType(typeof(GridView))]
            public static string GetDisplayMemberMember(DependencyObject obj)
            {
                return (string)obj.GetValue(DisplayMemberMemberProperty);
            }
    
            public static void SetDisplayMemberMember(DependencyObject obj, string value)
            {
                obj.SetValue(DisplayMemberMemberProperty, value);
            }
    
            // Using a DependencyProperty as the backing store for DisplayMember.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty DisplayMemberMemberProperty =
                DependencyProperty.RegisterAttached("DisplayMemberMember", typeof(string), typeof(GridViewColumns), new UIPropertyMetadata(null));
    
    
            private static void ColumnsSourceChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
            {
                GridView gridView = obj as GridView;
                if (gridView != null)
                {
                    gridView.Columns.Clear();
    
                    if (e.OldValue != null)
                    {
                        ICollectionView view = CollectionViewSource.GetDefaultView(e.OldValue);
                        if (view != null)
                            RemoveHandlers(gridView, view);
                    }
    
                    if (e.NewValue != null)
                    {
                        ICollectionView view = CollectionViewSource.GetDefaultView(e.NewValue);
                        if (view != null)
                        {
                            AddHandlers(gridView, view);
                            CreateColumns(gridView, view);
                        }
                    }
                }
            }
    
            private static IDictionary<ICollectionView, List<GridView>> _gridViewsByColumnsSource =
                new Dictionary<ICollectionView, List<GridView>>();
    
            private static List<GridView> GetGridViewsForColumnSource(ICollectionView columnSource)
            {
                List<GridView> gridViews;
                if (!_gridViewsByColumnsSource.TryGetValue(columnSource, out gridViews))
                {
                    gridViews = new List<GridView>();
                    _gridViewsByColumnsSource.Add(columnSource, gridViews);
                }
                return gridViews;
            }
    
            private static void AddHandlers(GridView gridView, ICollectionView view)
            {
                GetGridViewsForColumnSource(view).Add(gridView);
                view.CollectionChanged += ColumnsSource_CollectionChanged;
            }
    
            private static void CreateColumns(GridView gridView, ICollectionView view)
            {
                foreach (var item in view)
                {
                    GridViewColumn column = CreateColumn(gridView, item);
                    gridView.Columns.Add(column);
                }
            }
    
            private static void RemoveHandlers(GridView gridView, ICollectionView view)
            {
                view.CollectionChanged -= ColumnsSource_CollectionChanged;
                GetGridViewsForColumnSource(view).Remove(gridView);
            }
    
            private static void ColumnsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                ICollectionView view = sender as ICollectionView;
                var gridViews = GetGridViewsForColumnSource(view);
                if (gridViews == null || gridViews.Count == 0)
                    return;
    
                switch (e.Action)
                {
                    case NotifyCollectionChangedAction.Add:
                        foreach (var gridView in gridViews)
                        {
                            for (int i = 0; i < e.NewItems.Count; i++)
                            {
                                GridViewColumn column = CreateColumn(gridView, e.NewItems[i]);
                                gridView.Columns.Insert(e.NewStartingIndex + i, column);
                            }
                        }
                        break;
                    case NotifyCollectionChangedAction.Move:
                        foreach (var gridView in gridViews)
                        {
                            List<GridViewColumn> columns = new List<GridViewColumn>();
                            for (int i = 0; i < e.OldItems.Count; i++)
                            {
                                GridViewColumn column = gridView.Columns[e.OldStartingIndex + i];
                                columns.Add(column);
                            }
                            for (int i = 0; i < e.NewItems.Count; i++)
                            {
                                GridViewColumn column = columns[i];
                                gridView.Columns.Insert(e.NewStartingIndex + i, column);
                            }
                        }
                        break;
                    case NotifyCollectionChangedAction.Remove:
                        foreach (var gridView in gridViews)
                        {
                            for (int i = 0; i < e.OldItems.Count; i++)
                            {
                                gridView.Columns.RemoveAt(e.OldStartingIndex);
                            }
                        }
                        break;
                    case NotifyCollectionChangedAction.Replace:
                        foreach (var gridView in gridViews)
                        {
                            for (int i = 0; i < e.NewItems.Count; i++)
                            {
                                GridViewColumn column = CreateColumn(gridView, e.NewItems[i]);
                                gridView.Columns[e.NewStartingIndex + i] = column;
                            }
                        }
                        break;
                    case NotifyCollectionChangedAction.Reset:
                        foreach (var gridView in gridViews)
                        {
                            gridView.Columns.Clear();
                            CreateColumns(gridView, sender as ICollectionView);
                        }
                        break;
                    default:
                        break;
                }
            }
    
            private static GridViewColumn CreateColumn(GridView gridView, object columnSource)
            {
                GridViewColumn column = new GridViewColumn();
                string headerTextMember = GetHeaderTextMember(gridView);
                string displayMemberMember = GetDisplayMemberMember(gridView);
                if (!string.IsNullOrEmpty(headerTextMember))
                {
                    column.Header = GetPropertyValue(columnSource, headerTextMember);
                }
                if (!string.IsNullOrEmpty(displayMemberMember))
                {
                    string propertyName = GetPropertyValue(columnSource, displayMemberMember) as string;
                    column.DisplayMemberBinding = new Binding(propertyName);
                }
                return column;
            }
    
            private static object GetPropertyValue(object obj, string propertyName)
            {
                if (obj != null)
                {
                    PropertyInfo prop = obj.GetType().GetProperty(propertyName);
                    if (prop != null)
                        return prop.GetValue(obj, null);
                }
                return null;
            }
        }
    }
    

    视图模型

    class PersonsViewModel
    {
        public PersonsViewModel()
        {
            this.Persons = new ObservableCollection<Person>
            {
                new Person
                {
                    Name = "Doe",
                    FirstName = "John",
                    DateOfBirth = new DateTime(1981, 9, 12)
                },
                new Person
                {
                    Name = "Black",
                    FirstName = "Jack",
                    DateOfBirth = new DateTime(1950, 1, 15)
                },
                new Person
                {
                    Name = "Smith",
                    FirstName = "Jane",
                    DateOfBirth = new DateTime(1987, 7, 23)
                }
            };
    
            this.Columns = new ObservableCollection<ColumnDescriptor>
            {
                new ColumnDescriptor { HeaderText = "Last name", DisplayMember = "Name" },
                new ColumnDescriptor { HeaderText = "First name", DisplayMember = "FirstName" },
                new ColumnDescriptor { HeaderText = "Date of birth", DisplayMember = "DateOfBirth" }
            };
        }
    
        public ObservableCollection<Person> Persons { get; private set; }
    
        public ObservableCollection<ColumnDescriptor> Columns { get; private set; }
    
        private ICommand _addColumnCommand;
        public ICommand AddColumnCommand
        {
            get
            {
                if (_addColumnCommand == null)
                {
                    _addColumnCommand = new DelegateCommand<string>(
                        s =>
                        {
                            this.Columns.Add(new ColumnDescriptor { HeaderText = s, DisplayMember = s });
                        });
                }
                return _addColumnCommand;
            }
        }
    
        private ICommand _removeColumnCommand;
        public ICommand RemoveColumnCommand
        {
            get
            {
                if (_removeColumnCommand == null)
                {
                    _removeColumnCommand = new DelegateCommand<string>(
                        s =>
                        {
                            this.Columns.Remove(this.Columns.FirstOrDefault(d => d.DisplayMember == s));
                        });
                }
                return _removeColumnCommand;
            }
        }
    }
    

    XAML:

        <ListView ItemsSource="{Binding Persons}" Grid.Row="0">
            <ListView.View>
                <GridView local:GridViewColumns.HeaderTextMember="HeaderText"
                          local:GridViewColumns.DisplayMemberMember="DisplayMember"
                          local:GridViewColumns.ColumnsSource="{Binding Columns}"/>
            </ListView.View>
        </ListView>
    

    请注意,ColumnDescriptor 类实际上并不需要,我只是为了清楚起见添加了它,但任何类型都可以(包括匿名类型)。您只需要指定哪些属性保存标题文本并显示成员名称。

    另外,请记住,它尚未经过全面测试,因此可能需要修复一些问题...

    【讨论】:

    • 呃,我不确定我是否理解如何实现您的建议。
    • 哇,太好了,谢谢!我查了一下,你没有添加ColumnDescriptor的声明,但是正如你所说,应该很容易创建。我会在至少了解一点后立即通知;)
    • 酷,按预期工作,太棒了!非常感谢您提供的详尽示例!
    • 很好的例子!我将您的示例代码复制/粘贴到了一个不同的项目中,它就像一个魅力,谢谢!
    • +1 很糟糕,它必须是这种方式,但效果很好。
    【解决方案2】:

    我采用了 Thomas Levesque 的出色解决方案并对其进行了修改,以删除 GridViews 的静态集合,并添加了设置列宽和字符串格式的功能,所以我想分享我的代码。

    修改后的附加属性类:

    public static class GridViewColumnCollection
    {
        public static readonly DependencyProperty ColumnCollectionBehaviourProperty =
            DependencyProperty.RegisterAttached("ColumnCollectionBehaviour", typeof(GridViewColumnCollectionBehaviour), typeof(GridViewColumnCollection), new UIPropertyMetadata(null));
    
        public static readonly DependencyProperty ColumnsSourceProperty =
            DependencyProperty.RegisterAttached("ColumnsSource", typeof(object), typeof(GridViewColumnCollection), new UIPropertyMetadata(null, GridViewColumnCollection.ColumnsSourceChanged));
    
        public static readonly DependencyProperty DisplayMemberFormatMemberProperty =
            DependencyProperty.RegisterAttached("DisplayMemberFormatMember", typeof(string), typeof(GridViewColumnCollection), new UIPropertyMetadata(null, GridViewColumnCollection.DisplayMemberFormatMemberChanged));
    
        public static readonly DependencyProperty DisplayMemberMemberProperty =
            DependencyProperty.RegisterAttached("DisplayMemberMember", typeof(string), typeof(GridViewColumnCollection), new UIPropertyMetadata(null, GridViewColumnCollection.DisplayMemberMemberChanged));
    
        public static readonly DependencyProperty HeaderTextMemberProperty =
            DependencyProperty.RegisterAttached("HeaderTextMember", typeof(string), typeof(GridViewColumnCollection), new UIPropertyMetadata(null, GridViewColumnCollection.HeaderTextMemberChanged));
    
        public static readonly DependencyProperty WidthMemberProperty =
            DependencyProperty.RegisterAttached("WidthMember", typeof(string), typeof(GridViewColumnCollection), new UIPropertyMetadata(null, GridViewColumnCollection.WidthMemberChanged));
    
        [AttachedPropertyBrowsableForType(typeof(GridView))]
        public static GridViewColumnCollectionBehaviour GetColumnCollectionBehaviour(DependencyObject obj)
        {
            return (GridViewColumnCollectionBehaviour)obj.GetValue(ColumnCollectionBehaviourProperty);
        }
    
        public static void SetColumnCollectionBehaviour(DependencyObject obj, GridViewColumnCollectionBehaviour value)
        {
            obj.SetValue(ColumnCollectionBehaviourProperty, value);
        }
    
        [AttachedPropertyBrowsableForType(typeof(GridView))]
        public static object GetColumnsSource(DependencyObject obj)
        {
            return (object)obj.GetValue(ColumnsSourceProperty);
        }
    
        public static void SetColumnsSource(DependencyObject obj, object value)
        {
            obj.SetValue(ColumnsSourceProperty, value);
        }
    
        [AttachedPropertyBrowsableForType(typeof(GridView))]
        public static string GetDisplayMemberFormatMember(DependencyObject obj)
        {
            return (string)obj.GetValue(DisplayMemberFormatMemberProperty);
        }
    
        public static void SetDisplayMemberFormatMember(DependencyObject obj, string value)
        {
            obj.SetValue(DisplayMemberFormatMemberProperty, value);
        }
    
        [AttachedPropertyBrowsableForType(typeof(GridView))]
        public static string GetDisplayMemberMember(DependencyObject obj)
        {
            return (string)obj.GetValue(DisplayMemberMemberProperty);
        }
    
        public static void SetDisplayMemberMember(DependencyObject obj, string value)
        {
            obj.SetValue(DisplayMemberMemberProperty, value);
        }
    
        [AttachedPropertyBrowsableForType(typeof(GridView))]
        public static string GetHeaderTextMember(DependencyObject obj)
        {
            return (string)obj.GetValue(HeaderTextMemberProperty);
        }
    
        public static void SetHeaderTextMember(DependencyObject obj, string value)
        {
            obj.SetValue(HeaderTextMemberProperty, value);
        }
    
        [AttachedPropertyBrowsableForType(typeof(GridView))]
        public static string GetWidthMember(DependencyObject obj)
        {
            return (string)obj.GetValue(WidthMemberProperty);
        }
    
        public static void SetWidthMember(DependencyObject obj, string value)
        {
            obj.SetValue(WidthMemberProperty, value);
        }
    
        private static void ColumnsSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            GridViewColumnCollection.GetOrCreateColumnCollectionBehaviour(sender).ColumnsSource = e.NewValue;
        }
    
        private static void DisplayMemberFormatMemberChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            GridViewColumnCollection.GetOrCreateColumnCollectionBehaviour(sender).DisplayMemberFormatMember = e.NewValue as string;
        }
    
        private static void DisplayMemberMemberChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            GridViewColumnCollection.GetOrCreateColumnCollectionBehaviour(sender).DisplayMemberMember = e.NewValue as string;
        }
    
        private static void HeaderTextMemberChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            GridViewColumnCollection.GetOrCreateColumnCollectionBehaviour(sender).HeaderTextMember = e.NewValue as string;
        }
    
        private static void WidthMemberChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            GridViewColumnCollection.GetOrCreateColumnCollectionBehaviour(sender).WidthMember = e.NewValue as string;
        }
    
        private static GridViewColumnCollectionBehaviour GetOrCreateColumnCollectionBehaviour(DependencyObject source)
        {
            GridViewColumnCollectionBehaviour behaviour = GetColumnCollectionBehaviour(source);
    
            if (behaviour == null)
            {
                GridView typedSource = source as GridView;
    
                if (typedSource == null)
                {
                    throw new Exception("This property can only be set on controls deriving GridView");
                }
    
                behaviour = new GridViewColumnCollectionBehaviour(typedSource);
    
                SetColumnCollectionBehaviour(typedSource, behaviour);
            }
    
            return behaviour;
        }
    }
    

    行为(这是针对每个 GridView 存储的,无需集中存储集合-GridView 映射):

    public class GridViewColumnCollectionBehaviour
    {
        private object columnsSource;
        private GridView gridView;
    
        public GridViewColumnCollectionBehaviour(GridView gridView)
        {
            this.gridView = gridView;
        }
    
        public object ColumnsSource
        {
            get { return this.columnsSource; }
            set
            {
                object oldValue = this.columnsSource;
                this.columnsSource = value;
                this.ColumnsSourceChanged(oldValue, this.columnsSource);
            }
        }
    
        public string DisplayMemberFormatMember { get; set; }
    
        public string DisplayMemberMember { get; set; }
    
        public string HeaderTextMember { get; set; }
    
        public string WidthMember { get; set; }
    
        private void AddHandlers(ICollectionView collectionView)
        {
            collectionView.CollectionChanged += this.ColumnsSource_CollectionChanged;
        }
    
        private void ColumnsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            ICollectionView view = sender as ICollectionView;
    
            if (this.gridView == null)
            {
                return;
            }
    
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    for (int i = 0; i < e.NewItems.Count; i++)
                    {
                        GridViewColumn column = CreateColumn(e.NewItems[i]);
                        gridView.Columns.Insert(e.NewStartingIndex + i, column);
                    }
                    break;
                case NotifyCollectionChangedAction.Move:
                    List<GridViewColumn> columns = new List<GridViewColumn>();
    
                    for (int i = 0; i < e.OldItems.Count; i++)
                    {
                        GridViewColumn column = gridView.Columns[e.OldStartingIndex + i];
                        columns.Add(column);
                    }
    
                    for (int i = 0; i < e.NewItems.Count; i++)
                    {
                        GridViewColumn column = columns[i];
                        gridView.Columns.Insert(e.NewStartingIndex + i, column);
                    }
                    break;
                case NotifyCollectionChangedAction.Remove:
                    for (int i = 0; i < e.OldItems.Count; i++)
                    {
                        gridView.Columns.RemoveAt(e.OldStartingIndex);
                    }
                    break;
                case NotifyCollectionChangedAction.Replace:
                    for (int i = 0; i < e.NewItems.Count; i++)
                    {
                        GridViewColumn column = CreateColumn(e.NewItems[i]);
    
                        gridView.Columns[e.NewStartingIndex + i] = column;
                    }
                    break;
                case NotifyCollectionChangedAction.Reset:
                    gridView.Columns.Clear();
                    CreateColumns(sender as ICollectionView);
                    break;
                default:
                    break;
            }
        }
    
        private void ColumnsSourceChanged(object oldValue, object newValue)
        {
            if (this.gridView != null)
            {
                gridView.Columns.Clear();
    
                if (oldValue != null)
                {
                    ICollectionView view = CollectionViewSource.GetDefaultView(oldValue);
    
                    if (view != null)
                    {
                        this.RemoveHandlers(view);
                    }
                }
    
                if (newValue != null)
                {
                    ICollectionView view = CollectionViewSource.GetDefaultView(newValue);
    
                    if (view != null)
                    {
                        this.AddHandlers(view);
    
                        this.CreateColumns(view);
                    }
                }
            }
        }
    
        private GridViewColumn CreateColumn(object columnSource)
        {
            GridViewColumn column = new GridViewColumn();
    
            if (!string.IsNullOrEmpty(this.HeaderTextMember))
            {
                column.Header = GetPropertyValue(columnSource, this.HeaderTextMember);
            }
    
            if (!string.IsNullOrEmpty(this.DisplayMemberMember))
            {
                string propertyName = GetPropertyValue(columnSource, this.DisplayMemberMember) as string;
    
                string format = null;
    
                if (!string.IsNullOrEmpty(this.DisplayMemberFormatMember))
                {
                    format = GetPropertyValue(columnSource, this.DisplayMemberFormatMember) as string;
                }
    
                if (string.IsNullOrEmpty(format))
                {
                    format = "{0}";
                }
    
                column.DisplayMemberBinding = new Binding(propertyName) { StringFormat = format };
            }
    
            if (!string.IsNullOrEmpty(this.WidthMember))
            {
                double width = (double)GetPropertyValue(columnSource, this.WidthMember);
                column.Width = width;
            }
    
            return column;
        }
    
        private void CreateColumns(ICollectionView collectionView)
        {
            foreach (object item in collectionView)
            {
                GridViewColumn column = this.CreateColumn(item);
    
                this.gridView.Columns.Add(column);
            }
        }
    
        private object GetPropertyValue(object obj, string propertyName)
        {
            object returnVal = null;
    
            if (obj != null)
            {
                PropertyInfo prop = obj.GetType().GetProperty(propertyName);
    
                if (prop != null)
                {
                    returnVal = prop.GetValue(obj, null);
                }
            }
    
            return returnVal;
        }
    
        private void RemoveHandlers(ICollectionView collectionView)
        {
            collectionView.CollectionChanged -= this.ColumnsSource_CollectionChanged;
        }
    }
    

    【讨论】:

    • 单元格模板呢?
    • 如果您想在创建的 GridViewColumns 上设置其他属性,然后修改附加的行为类以包含新属性并相应地修改行为类(特别是 CreateColumn)。首先,请尝试查看附加行为类(例如 WidthMember)上的属性值发生更改时会发生什么,并从那里进行跟踪。
    【解决方案3】:

    我认为这段代码会导致一些内存泄漏问题;正如您的类 GridViewColumns 所述,您已经定义了一个静态字典“_gridViewsByColumnsSource”,其中包含网格视图及其列源引用;所以这是对添加的网格视图和列源的强烈参考;因为这个字典是静态的,所以似乎一直有一个静态引用“指向”gridviews和columns源数据,如果gridview定义的屏幕关闭,则启动GC时gridview无法被GC收集;随着打开的类似屏幕越来越多,越来越多的gridview和它的columns源数据无法采集,最后会出现内存泄漏。

    【讨论】:

    • 我用内存分析器确认了这个问题。您可以通过在释放控件时将 ColumnsSource 设置为 null 来解决此问题,这会导致引用被删除(在 RemoveHandlers 方法中),或者更改 _gridViewsByColumnsSource 以保存 WeakReference 对象列表并为 ICollectionView 实现弱事件侦听器。 CollectionChanged 事件。
    • 实际上你不需要弱事件监听器...只需要一个对 GridView 的 WeakReference。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-23
    相关资源
    最近更新 更多