簡體   English   中英

如何獲得 ListView GridViewColumn 來填充網格中的剩余空間?

[英]How can I get a ListView GridViewColumn to fill the remaining space in my grid?

我想創建一個具有固定寬度的兩列和第三列以填充剩余空間的 ListView。 所以是這樣的:

<ListView>
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Name" Width="*" />
            <GridViewColumn Header="Age" Width="50" />
            <GridViewColumn Header="Gender" Width="50" />
        </GridView>
    </ListView.View>
</ListView>

問題是我找不到讓Name列填充剩余空間的方法,因為將寬度設置為*不起作用。 看起來有一種方法可以使用值轉換器來做到這一點,但似乎應該有一種更簡單的方法。 與 DataGrid 控件一樣,您可以使用* s 指定列的寬度。

我試圖實現相同的目標,但后來決定我希望我的 ListView 列消耗 ListView 的一部分,結果是所有列都消耗一部分空間,並且所有空間都在 ListView 中消耗。 您可以將其設置為在最后一列具有任何您喜歡的百分比,以直接實現“填充最后一列的剩余空間”目標。

我發現這種方法相當健壯和可靠(即使在調整大小時!)所以我想我可以分享。

對於此示例,我的 ListView 中有四列。 您只需要使用以下事件處理程序在 ListView 中注冊SizeChanged事件:

private void ProductsListView_SizeChanged(object sender, SizeChangedEventArgs e)
{
    ListView listView = sender as ListView;
    GridView gView = listView.View as GridView;

    var workingWidth = listView.ActualWidth - SystemParameters.VerticalScrollBarWidth; // take into account vertical scrollbar
    var col1 = 0.50;
    var col2 = 0.20;
    var col3 = 0.15;
    var col4 = 0.15;

    gView.Columns[0].Width = workingWidth*col1;
    gView.Columns[1].Width = workingWidth*col2;
    gView.Columns[2].Width = workingWidth*col3;
    gView.Columns[3].Width = workingWidth*col4;
}

在研究類似問題時遇到了這個問題,我的問題是我希望所有列都是“自動”,期望第一個,這只會填補額外的空間,所以我擴展了 GONeale 的解決方案。

private void ListView_SizeChanged(object sender, SizeChangedEventArgs e)
{
    ListView _ListView = sender as ListView;
    GridView _GridView = _ListView.View as GridView;
    var _ActualWidth = _ListView.ActualWidth - SystemParameters.VerticalScrollBarWidth;
    for (Int32 i = 1; i < _GridView.Columns.Count; i++)
    {
        _ActualWidth = _ActualWidth - _GridView.Columns[i].ActualWidth;
    }
    _GridView.Columns[0].Width = _ActualWidth;
}

那么 XAML 很簡單:

...
<ListView.View>
    <GridView>
        <GridViewColumn Header="Title" />
        <GridViewColumn Header="Artist" Width="Auto" />
        <GridViewColumn Header="Album" Width="Auto" />
        <GridViewColumn Header="Genre" Width="Auto" />
    </GridView>
</ListView.View>
...

此代碼也可以更通用地使用,因為列的數量不是硬編碼的,並且通過一些調整,您可能可以通過某種邏輯來定義“填充列”。

希望它可以幫助某人:)

問題是 GridViewColumn 的列寬是雙倍的,而不是 GridLength 對象,並且沒有適當的轉換來處理 *. 不確定這是否是 WPF 團隊的疏忽。 你會認為它應該被支持。

除了轉換器,我見過的唯一其他方法是在這里: http ://www.ontheblog.net/CMS/Default.aspx?tabid=36&EntryID=37。

兩者都是不需要的額外工作。 我在 ListView 和 GridView 組合中發現了其他“奇怪”的東西,所以我停止使用它們。 如果我需要一個數據網格,我使用我們許可的第 3 方,如果我需要一個復雜的 ListBox 樣式菜單,我只使用一個模板化的 ListBox。

另一個答案中提到的 David Hanson-Greville 的OnTheBlog的解決方案不再可用,即使該博客仍然存在。 我能夠在 Wayback Machine 上找到它,並且經過一些審核,它是:

訣竅是您在 ListView 上設置 Stretch=true ,它將拉伸寬度不相等的列。

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;

namespace Demo.Extension.Properties
{
    ///
    /// ListViewColumnStretch
    ///
    public class ListViewColumns : DependencyObject
    {
        ///
        /// IsStretched Dependancy property which can be attached to gridview columns.
        ///
        public static readonly DependencyProperty StretchProperty =
            DependencyProperty.RegisterAttached("Stretch",
            typeof(bool),
            typeof(ListViewColumns),
            new UIPropertyMetadata(true, null, OnCoerceStretch));

        ///
        /// Gets the stretch.
        ///
        /// The obj.
        ///
        public static bool GetStretch(DependencyObject obj)
        {
            return (bool)obj.GetValue(StretchProperty);
        }

        ///
        /// Sets the stretch.
        ///
        /// The obj.
        /// if set to true [value].
        public static void SetStretch(DependencyObject obj, bool value)
        {
            obj.SetValue(StretchProperty, value);
        }

        ///
        /// Called when [coerce stretch].
        ///
        ///If this callback seems unfamilar then please read
        /// the great blog post by Paul Jackson found here.
        /// http://compilewith.net/2007/08/wpf-dependency-properties.html
        /// The source.
        /// The value.
        ///
        public static object OnCoerceStretch(DependencyObject source, object value)
        {
            ListView lv = (source as ListView);

            //Ensure we dont have an invalid dependancy object of type ListView.
            if (lv == null)
            {
                throw new ArgumentException("This property may only be used on ListViews");
            }

            //Setup our event handlers for this list view.
            lv.Loaded += new RoutedEventHandler(lv_Loaded);
            lv.SizeChanged += new SizeChangedEventHandler(lv_SizeChanged);
            return value;
        }

        ///
        /// Handles the SizeChanged event of the lv control.
        ///
        /// The source of the event.
        /// The instance containing the event data.
        private static void lv_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            ListView lv = (sender as ListView);
            if (lv.IsLoaded)
            {
                //Set our initial widths.
                SetColumnWidths(lv);
            }
        }

        ///
        /// Handles the Loaded event of the lv control.
        ///
        /// The source of the event.
        /// The instance containing the event data.
        private static void lv_Loaded(object sender, RoutedEventArgs e)
        {
            ListView lv = (sender as ListView);
            //Set our initial widths.
            SetColumnWidths(lv);
        }

        ///
        /// Sets the column widths.
        ///
        private static void SetColumnWidths(ListView listView)
        {
            //Pull the stretch columns fromt the tag property.
            List<GridViewColumn> columns = (listView.Tag as List<GridViewColumn>);
            double specifiedWidth = 0;
            GridView gridView = listView.View as GridView;
            if (gridView != null)
            {
                if (columns == null)
                {
                    //Instance if its our first run.
                    columns = new List<GridViewColumn>();
                    // Get all columns with no width having been set.
                    foreach (GridViewColumn column in gridView.Columns)
                    {
                        if (!(column.Width >= 0))
                        {
                            columns.Add(column);
                        }
                        else
                        {
                            specifiedWidth += column.ActualWidth;
                        }
                    }
                }
                else
                {
                    // Get all columns with no width having been set.
                    foreach (GridViewColumn column in gridView.Columns)
                    {
                        if (!columns.Contains(column))
                        {
                            specifiedWidth += column.ActualWidth;
                        }
                    }
                }

                // Allocate remaining space equally.
                foreach (GridViewColumn column in columns)
                {
                    double newWidth = (listView.ActualWidth - specifiedWidth) / columns.Count;
                    if (newWidth >= 10)
                    {
                        column.Width = newWidth - 10;
                    }
                }

                //Store the columns in the TAG property for later use.
                listView.Tag = columns;
            }
        }
    }
}

然后,您只需將命名空間添加到 XAML 文件:

xmlns:Extensions="clr-namespace:Demo.Extension.Properties"

並在您的列表視圖中使用它:

<ListView ItemsSource="{Binding Path=Items}" DisplayMemberPath="Name"
                          ScrollViewer.VerticalScrollBarVisibility="Auto"
                          Grid.Column="0" Margin="8" Extensions:ListViewColumns.Stretch="true">

我的需要是讓所有列的寬度相同。 上述解決方案很好,但我更喜歡將這樣的東西包裝在附加屬性中(MVVM、可重用性等)。 這是我的代碼,如果有幫助的話。

    public class StarSizeHelper {

    private static readonly List<FrameworkElement> s_knownElements = new List<FrameworkElement>();

    public static bool GetIsEnabled(DependencyObject d) {
        return (bool) d.GetValue(IsEnabledProperty);
    }

    public static void SetIsEnabled(ListView d, bool value) {
        d.SetValue(IsEnabledProperty, value);
    }

    public static readonly DependencyProperty IsEnabledProperty =
        DependencyProperty.RegisterAttached("IsEnabled", 
                                            typeof(bool), 
                                            typeof(StarSizeHelper),
                                            new FrameworkPropertyMetadata(IsEnabledChanged));

    public static void IsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {

        var ctl = d as ListView;
        if (ctl == null) {
            throw new Exception("IsEnabled attached property only works on a ListView type");
        }

        RememberElement(ctl);
    }

    private static void RememberElement(ListView ctl) {

        if (! s_knownElements.Contains(ctl)) {
            s_knownElements.Add(ctl);

            RegisterEvents(ctl);
        } 
        // nothing to do if elt is known
    }

    private static void OnUnloaded(object sender, RoutedEventArgs e) {

        FrameworkElement ctl = (FrameworkElement) sender;
        ForgetControl(ctl);
    }

    private static void ForgetControl(FrameworkElement fe) {

        s_knownElements.Remove(fe);
        UnregisterEvents(fe);
    }

    private static void RegisterEvents(FrameworkElement fe) {
        fe.Unloaded += OnUnloaded;
        fe.SizeChanged += OnSizeChanged;
    }

    private static void UnregisterEvents(FrameworkElement fe) {
        fe.Unloaded -= OnUnloaded;
        fe.SizeChanged -= OnSizeChanged;
    }

    private static void OnSizeChanged(object sender, SizeChangedEventArgs e) {

        ListView listView = sender as ListView;
        if (listView == null) {
            return; // should not happen
        }
        GridView gView = listView.View as GridView;
        if (gView == null) {
            return; // should not happen
        }

        var workingWidth = listView.ActualWidth - SystemParameters.VerticalScrollBarWidth -10; // take into account vertical scrollbar
        var colWidth = workingWidth / gView.Columns.Count;
        foreach (GridViewColumn column in gView.Columns) {
            column.Width = colWidth;
        }
    }
}

為了使用它:

<ListView ... StarSizeHelper.IsEnabled="true" ... />

(當然,您仍然需要修復 XAML 中的命名空間聲明)

您可以在 OnSizeChanged 方法中調整您的大小需求。

我意識到這是一個老問題,但我在嘗試為ListView / GridView中的多個列獲取基於的解決方案時發現了這篇文章。 所以我想我可以幫助一些未來的人,因為它也會回答這個問題。

我最初實現了一個WidthConverter但這有一個明顯的限制,即最后一列不會“填充”,並且從未保證該行會適合它的空間,但這里是為那些好奇的人准備的:

public class WidthConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var actualWidth = (double)value;
            var desiredPercentage = SafeConvert(parameter);

            if (actualWidth == 0.0 || desiredPercentage == 0.0) return double.NaN;

            var result = actualWidth * (desiredPercentage / 100);

            return result;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        private double SafeConvert(object pInput)
        {
            if (pInput == null) return default;

            if (double.TryParse(pInput.ToString(), out var result))
            {
                return result;
            }

            return default;
        }
    }
<GridViewColumn Header="My Header"
                DisplayMemberBinding="{Binding MyColumnData}"
                Width="{Binding Path=ActualWidth, ElementName=MyListView, Converter={StaticResource WidthConverter}, ConverterParameter='10.5'}" />

這是可行的,但它非常依賴於程序員對參數值進行硬編碼,並且在綁定需要ListView元素的ActualWidth屬性的意義上說是混亂的。 最終,它相當受限於它實際可以做的事情,特別是考慮到大多數 WPF 人員在星大小方面習慣於使用GridLength

我從這里和其他地方發布的解決方案中獲取了各種零碎的信息,並開發了一個基於附加屬性和行為的 MVVM 友好解決方案。

我使用GridLength創建了一個附加屬性,以利用現有的絕對/自動/星形邏輯匹配我們都習慣的 XAML 寬度模式。

public class ColumnAttachedProperties : DependencyObject
{
    public static readonly DependencyProperty GridLength_WidthProperty = DependencyProperty.RegisterAttached(
        name: "GridLength_Width",
        propertyType: typeof(GridLength),
        ownerType: typeof(ColumnAttachedProperties),
        defaultMetadata: new FrameworkPropertyMetadata(GridLength.Auto));

    public static GridLength GetGridLength_Width(DependencyObject dependencyObject) 
        => (GridLength)dependencyObject.GetValue(GridLength_WidthProperty);

    public static void SetGridLength_Width(DependencyObject dependencyObject, string value) 
        => dependencyObject.SetValue(GridLength_WidthProperty, value);
}

附加行為掛鈎到 Loaded 和 SizeChanged 事件,並執行一些共享邏輯來調整列的大小。

本質上,在第一次遍歷列時,它將計算星值(但尚未為星列設置寬度),然后在第二次遍歷中以星列為目標,將寬度設置為剩余值的百分比可用寬度。 我相信這可以通過某種方式進行優化。

public static class ListViewStarSizingAttachedBehavior
{
    public static readonly DependencyProperty UseGridLength_WidthProperty = DependencyProperty.RegisterAttached(
        name: "UseGridLength_Width",
        propertyType: typeof(bool),
        ownerType: typeof(ListViewStarSizingAttachedBehavior),
        new UIPropertyMetadata(false, RegisterEventHandlers));

    public static bool GetUseGridLength_Width(DependencyObject dependencyObject) 
        => (bool)dependencyObject.GetValue(UseGridLength_WidthProperty);
    public static void SetUseGridLength_Width(DependencyObject dependencyObject, bool value) 
        => dependencyObject.SetValue(UseGridLength_WidthProperty, value);

    private static void RegisterEventHandlers(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is ListView listView)
        {
            if (e.NewValue is bool booleanValue && booleanValue == true)
            {
                listView.SizeChanged += ListView_SizeChanged;
                listView.Loaded += ListView_Loaded;
            }
            else
            {
                listView.SizeChanged -= ListView_SizeChanged;
                listView.Loaded -= ListView_Loaded;
            }
        }
    }

    private static void ListView_Loaded(object sender, RoutedEventArgs e)
    {
        CalculateGridColumnWidths(sender);
    }

    private static void ListView_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        if (sender is ListView listView && !listView.IsLoaded) return;

        CalculateGridColumnWidths(sender);
    }

    private static void CalculateGridColumnWidths(object sender)
    {
        if (sender is ListView listView && listView.View is GridView gridView)
        {
            // the extra offset may need to be altered per your application.
            var scrollOffset = SystemParameters.VerticalScrollBarWidth - 7;

            var remainingWidth = listView.ActualWidth - scrollOffset;
            var starTotal = 0.0;
                
            foreach (var column in gridView.Columns)
            {
                var gridLength = ColumnAttachedProperties.GetGridLength_Width(column);

                if (gridLength.IsStar)
                {
                    // Get the cumlative star value while passing over the columns
                    // but don't set their width until absolute and auto have been set.
                    starTotal += gridLength.Value;
                    continue;
                }

                if (gridLength.IsAbsolute)
                {
                    column.Width = gridLength.Value;
                }
                else
                {
                    column.Width = double.NaN;
                }

                remainingWidth -= column.ActualWidth;
            }

            if (starTotal == 0.0) return;

            // now eval each star column
            foreach (var column in gridView.Columns)
            {
                var gridLength = ColumnAttachedProperties.GetGridLength_Width(column);

                if (!gridLength.IsStar) continue;

                var starPercent = (gridLength.Value / starTotal);
                column.Width = remainingWidth * starPercent;
            }
        }
    }
}

最后要在 XAML 中使用它, ListView需要實現附加行為,並且每個列都實現附加屬性。 但是,如果您將附加屬性保留在列之外,它將根據DependencyProperty的 defaultMetadata 默認為Auto

<ListView local:ListViewStarSizingAttachedBehavior.UseGridLength_Width="True"
          ItemsSource="{Binding MyItems}" >
    <ListView.View>
        <GridView>
            <GridView.Columns>
                <!-- Absolute -->
                <GridViewColumn local:ColumnAttachedProperties.GridLength_Width="250"
                                Header="Column One"
                                DisplayMemberBinding="{Binding Item1}" />
                
                <!-- Star -->               
                <GridViewColumn local:ColumnAttachedProperties.GridLength_Width="2*"
                                Header="Column Two"
                                DisplayMemberBinding="{Binding Item2}" />
                
                <!-- Auto -->               
                <GridViewColumn local:ColumnAttachedProperties.GridLength_Width="Auto"
                                Header="Column Three"
                                DisplayMemberBinding="{Binding Item3}" />

                <!-- Star -->
                <GridViewColumn local:ColumnAttachedProperties.GridLength_Width="*"
                                Header="Column Four"
                                DisplayMemberBinding="{Binding Item4}" />
            </GridView.Columns>
        </GridView>
    </ListView.View>
</ListView>

到目前為止似乎運作良好,但我確信有一些邊緣情況需要考慮。 XAML 比寬度轉換器更干凈。 總體結果也比已經發布的更靈活。

我的問題很相似,但我想修復第一列的寬度,我也不希望它在我添加或刪除列時中斷,即使在運行時也是如此。 感謝@Gary 在https://stackoverflow.com/a/14674830/492的提示

private void ResultsListView_SizeChanged(object sender, SizeChangedEventArgs e)
    {
      double newWidthForColumnsExceptFirstColumn = ResultsListView.ActualWidth - SystemParameters.VerticalScrollBarWidth - ResultsGridView.Columns[0].Width;
      int columnsCount = ResultsGridView.Columns.Count;
      Double newColumnWidth = newWidthForColumnsExceptFirstColumn / (columnsCount -1);

      for ( int col = 1; col < columnsCount; col++ ) // skip column [0]
      {
        ResultsGridView.Columns[col].Width = newColumnWidth;
      }
    }

這是一個解決方案,它允許多個 ListViews 利用通用的“調整大小”事件處理程序。

    //Using dictionarys as trackers allows us to have multiple ListViews use the same code
    private Dictionary<string, double> _fixedWidthTracker = new Dictionary<string, double>();
    private Dictionary<string, List<GridViewColumn>> _varWidthColTracker = new Dictionary<string, List<GridViewColumn>>();
    private void ListView_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        ListView lv = sender as ListView;
        if (lv != null)
        {
            //For validation during Debug
            VerifyName(lv);

            GridView gv = lv.View as GridView;
            if (gv != null)
            {
                if (!_varWidthColTracker.ContainsKey(lv.Name))
                {
                    _varWidthColTracker[lv.Name] = new List<GridViewColumn>();
                    _fixedWidthTracker[lv.Name] = 0;
                    foreach (GridViewColumn gvc in gv.Columns)
                    {
                        if (!double.IsNaN(gvc.Width)) _fixedWidthTracker[lv.Name] += gvc.Width; else _varWidthColTracker[lv.Name].Add(gvc);
                    }
                }
                double newWidthForColumns = e.NewSize.Width - SystemParameters.VerticalScrollBarWidth - _fixedWidthTracker[lv.Name];
                int columnsCount = gv.Columns.Count;
                int numberOfFixedWithColumns = columnsCount - _varWidthColTracker[lv.Name].Count;
                Double newColumnWidth = newWidthForColumns / (columnsCount - numberOfFixedWithColumns);

                foreach (GridViewColumn gvc in _varWidthColTracker[lv.Name])
                {
                    gvc.Width = newColumnWidth;
                }
            }
        }
    }

    /// <summary>
    /// Warns the developer if this object does not have
    /// a public property with the specified name. This 
    /// method does not exist in a Release build.
    /// </summary>
    [Conditional("DEBUG")]
    [DebuggerStepThrough]
    public void VerifyName(ListView listView)
    {
        if (String.IsNullOrEmpty(listView.Name))
        {
            string msg = "The Name attribute is required to be set on the ListView in order to Bind to this method";
            Debug.Fail(msg);
        }
    }

我舉了上面的例子(這很好)並稍微改進它以防止調整大小時出現運行時異常:

private void tpList_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            ListView listView = sender as ListView;
            GridView gView = listView.View as GridView;

            var workingWidth = listView.ActualWidth - (SystemParameters.VerticalScrollBarWidth + 20); // take into account vertical scrollbar
            var col1 = 0.50;
            var col2 = 0.50;

            var t1 = workingWidth * col1;
            var t2 = workingWidth * col2;
            gView.Columns[0].Width = t1 > 0 ? t1 : 1;
            gView.Columns[1].Width = t2 > 0 ? t2 : 1;

        }
    }

基於其他一些答案(例如 Timores 的答案),這是我通常使用的答案。 與那些不同的是,此解決方案允許相對大小和自動大小共存:

  public class GridViewColumnRelativeSize {
    public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(GridViewColumnRelativeSize), new FrameworkPropertyMetadata(IsEnabledPropertyChanged));
    public static bool GetIsEnabled(DependencyObject d) => (bool)d.GetValue(IsEnabledProperty);
    public static void SetIsEnabled(ListView d, bool value) => d.SetValue(IsEnabledProperty, value);

    public static void IsEnabledPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
      if (d is ListView list) {
        if (!KnownLists.Contains(list)) {
          KnownLists.Add(list);
          list.Unloaded += OnUnloaded;
          list.SizeChanged += OnSizeChanged;
        }
      }
      else
        throw new Exception("ListView expected");
    }

    public static readonly DependencyProperty RelativeWidthProperty = DependencyProperty.RegisterAttached("RelativeWidth", typeof(double), typeof(GridViewColumnRelativeSize), new FrameworkPropertyMetadata(double.NaN));
    public static double GetWidth(DependencyObject d) => (double)d.GetValue(RelativeWidthProperty);
    public static void SetWidth(GridViewColumn d, double value) => d.SetValue(RelativeWidthProperty, value);

    private static readonly List<FrameworkElement> KnownLists = new();

    private static void OnUnloaded(object sender, RoutedEventArgs e) {
      var element = (FrameworkElement)sender;
      _ = KnownLists.Remove(element);
      element.Unloaded -= OnUnloaded;
      element.SizeChanged -= OnSizeChanged;
    }

    private static void OnSizeChanged(object sender, SizeChangedEventArgs e) {
      if (sender is ListView listView) {
        bool isEnabled = listView.GetValue(IsEnabledProperty) is bool enabled && enabled;
        if (isEnabled && listView.View is GridView gridView) {
          double TotalUnits = gridView.Columns.Sum(column => {
            double width = (double)column.GetValue(RelativeWidthProperty);
            return double.IsNaN(width) ? 1 : width;
          });
          double ActualWidth = listView.ActualWidth - SystemParameters.VerticalScrollBarWidth;
          double UnitWidth = Math.Floor(ActualWidth / TotalUnits);
          foreach (var column in gridView.Columns) {
            double unit = (double)column.GetValue(RelativeWidthProperty);
            if (!double.IsNaN(unit))
              column.Width = unit * UnitWidth;
          }
        }
      }
    }

  }
<ListView ns:GridViewColumnRelativeSize.IsEnabled="True">
  <ListView.View>
    <GridView>
      <GridViewColumn />
      <GridViewColumn Width="50" />
      <GridViewColumn ns:GridViewColumnRelativeSize.Width="2" />
    </GridView>
  </ListView.View>

具有特定Width的列遵循大小,沒有Width的列將自動調整大小,具有GridViewColumnRelativeSize.Width的列將使用該值作為因子按比例調整大小。

暫無
暫無

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

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