আমি কীভাবে একটি ডাব্লুপিএফ ডেটাগ্রিডকে পরিবর্তনশীল সংখ্যক কলামগুলিতে আবদ্ধ করতে পারি?


124

আমার ডাব্লুপিএফ অ্যাপ্লিকেশন ডেটার সেটগুলি উত্পন্ন করে যাতে প্রতিবার বিভিন্ন সংখ্যক কলাম থাকতে পারে। আউটপুটে অন্তর্ভুক্ত হ'ল প্রতিটি কলামের বর্ণনা যা ফরম্যাটিং প্রয়োগ করতে ব্যবহৃত হবে। আউটপুটটির একটি সরলীকৃত সংস্করণ এমন কিছু হতে পারে:

class Data
{
    IList<ColumnDescription> ColumnDescriptions { get; set; }
    string[][] Rows { get; set; }
}

এই ক্লাসটি একটি ডাব্লুপিএফ ডেটাগ্রিডে ডেটা কনটেক্সট হিসাবে সেট করা আছে তবে আমি আসলে কলামগুলি প্রোগ্রামগতভাবে তৈরি করেছি:

for (int i = 0; i < data.ColumnDescriptions.Count; i++)
{
    dataGrid.Columns.Add(new DataGridTextColumn
    {
        Header = data.ColumnDescriptions[i].Name,
        Binding = new Binding(string.Format("[{0}]", i))
    });
}

এর পরিবর্তে XAML ফাইলে এই কোডটি ডেটা বাইন্ডিংয়ের সাথে প্রতিস্থাপনের কোনও উপায় আছে কি?

উত্তর:


127

ডেটাগ্রিডে বাঁধাই করা কলামগুলির জন্য এখানে একটি কার্যকারিতা রয়েছে। যেহেতু কলামগুলির সম্পত্তি পঠনযোগ্য, সবার মতোই লক্ষ্য করা যায়, আমি বিনেডেবল কলামগুলি নামে একটি সংযুক্ত সম্পত্তি তৈরি করেছি যা কালেকশনে পরিবর্তিত ইভেন্টের মাধ্যমে সংগ্রহ পরিবর্তনের সময় ডেটাগ্রিডে কলামগুলি আপডেট করে।

আমাদের যদি ডেটাগ্রিডকলামের এই সংগ্রহ থাকে

public ObservableCollection<DataGridColumn> ColumnCollection
{
    get;
    private set;
}

তারপরে আমরা বাইন্ডেবল কলামগুলি এর মতো কলাম কলামে আবদ্ধ করতে পারি

<DataGrid Name="dataGrid"
          local:DataGridColumnsBehavior.BindableColumns="{Binding ColumnCollection}"
          AutoGenerateColumns="False"
          ...>

সংযুক্ত সম্পত্তি বাইন্ডেবল কলামগুলি

public class DataGridColumnsBehavior
{
    public static readonly DependencyProperty BindableColumnsProperty =
        DependencyProperty.RegisterAttached("BindableColumns",
                                            typeof(ObservableCollection<DataGridColumn>),
                                            typeof(DataGridColumnsBehavior),
                                            new UIPropertyMetadata(null, BindableColumnsPropertyChanged));
    private static void BindableColumnsPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
    {
        DataGrid dataGrid = source as DataGrid;
        ObservableCollection<DataGridColumn> columns = e.NewValue as ObservableCollection<DataGridColumn>;
        dataGrid.Columns.Clear();
        if (columns == null)
        {
            return;
        }
        foreach (DataGridColumn column in columns)
        {
            dataGrid.Columns.Add(column);
        }
        columns.CollectionChanged += (sender, e2) =>
        {
            NotifyCollectionChangedEventArgs ne = e2 as NotifyCollectionChangedEventArgs;
            if (ne.Action == NotifyCollectionChangedAction.Reset)
            {
                dataGrid.Columns.Clear();
                foreach (DataGridColumn column in ne.NewItems)
                {
                    dataGrid.Columns.Add(column);
                }
            }
            else if (ne.Action == NotifyCollectionChangedAction.Add)
            {
                foreach (DataGridColumn column in ne.NewItems)
                {
                    dataGrid.Columns.Add(column);
                }
            }
            else if (ne.Action == NotifyCollectionChangedAction.Move)
            {
                dataGrid.Columns.Move(ne.OldStartingIndex, ne.NewStartingIndex);
            }
            else if (ne.Action == NotifyCollectionChangedAction.Remove)
            {
                foreach (DataGridColumn column in ne.OldItems)
                {
                    dataGrid.Columns.Remove(column);
                }
            }
            else if (ne.Action == NotifyCollectionChangedAction.Replace)
            {
                dataGrid.Columns[ne.NewStartingIndex] = ne.NewItems[0] as DataGridColumn;
            }
        };
    }
    public static void SetBindableColumns(DependencyObject element, ObservableCollection<DataGridColumn> value)
    {
        element.SetValue(BindableColumnsProperty, value);
    }
    public static ObservableCollection<DataGridColumn> GetBindableColumns(DependencyObject element)
    {
        return (ObservableCollection<DataGridColumn>)element.GetValue(BindableColumnsProperty);
    }
}

1
এমভিভিএম প্যাটার্নের জন্য দুর্দান্ত সমাধান
WPFKK

2
একটি নিখুঁত সমাধান! সম্ভবত আপনাকে বাইন্ডেবলকলামগুলি প্রোপার্টিচেন্জডে আরও কয়েকটি জিনিস করতে হবে: ১. ডেটাগ্রিড অ্যাক্সেস করার আগে নালার জন্য পরীক্ষা করুন এবং কেবল ডেটাগ্রিডে বাঁধাই সম্পর্কে ভাল ব্যাখ্যা সহ একটি ব্যতিক্রম নিক্ষেপ করুন। ২. মেমরি ফাঁস রোধ করতে কালেকশনযুক্ত ইভেন্ট থেকে নালার জন্য সাবস্ক্রাইব করা এবং বাতিল করতে e.OldValue দেখুন। শুধু আপনার বোঝার জন্য।
মাইক এশভা

3
আপনি একটি ইভেন্ট হ্যান্ডলার সাথে নিবন্ধন করুন CollectionChanged কলামগুলি সংগ্রহের ইভেন্ট , তবে আপনি কখনই এটি না। এইভাবে, DataGridভিউ-মডেলটি যতক্ষণ থাকবে ততক্ষণ বেঁচে থাকবে, এমনকি যদি DataGridপ্রথম স্থানটিতে থাকা নিয়ন্ত্রণ টেম্পলেটটি ইতিমধ্যে প্রতিস্থাপন করা হয়। যখন DataGridআর প্রয়োজন নেই তখন ইভেন্ট হ্যান্ডেলারটিকে আবার নিবন্ধভুক্ত করার কোনও গ্যারান্টিযুক্ত উপায় আছে কি ?
অথবা ম্যাপার

1
@ ওআর ম্যাপার: তাত্ত্বিকভাবে এটি রয়েছে তবে এটি কার্যকর হয় না: WeEEEVEEManager <পর্যবেক্ষণযোগ্য সংগ্রহ <দ্যতাগ্রিড কলাম>, নোটিফোলকোলকশন চ্যাঞ্জডইভেন্টআর্গস> .এডহ্যান্ডলার (কলাম, "কালেকশন-চেঞ্জড", (গুলি, নে) => {স্যুইচ ....});
খুব

6
এটি বেস সমাধান নয়। মূল কারণ হ'ল আপনি ভিউমোডেলে ইউআই ক্লাস ব্যবহার করছেন। এছাড়াও আপনি যখন কিছু পৃষ্ঠা স্যুইচিং তৈরি করার চেষ্টা করবেন তখন এটি কাজ করবে না। এই জাতীয় ডেটাগ্রিডের সাথে পৃষ্ঠায় ফিরে যাওয়ার সময় আপনি dataGrid.Columns.Add(column)একটি ডেটাগ্রিডের কলাম সংকলনে শিরোনাম 'এক্স' সহ লাইন ডেটাগ্রিড কলমটিতে একটি এক্সপেকশন পেয়ে যাবেন । ডেটাগ্রিডগুলি কলামগুলি ভাগ করতে পারে না এবং এতে নকল কলামের দৃষ্টান্ত থাকতে পারে না।
রুসলান এফ।

19

আমি আমার গবেষণা চালিয়েছি এবং এটি করার কোনও যুক্তিসঙ্গত উপায় খুঁজে পাইনি। ডেটাগ্রিডে কলামগুলির সম্পত্তি এমন কিছু নয় যা আমি বাঁধতে পারি, আসলে এটি কেবল পঠিত।

ব্রায়ান প্রস্তাব দিয়েছিল অটিজেনারেট কলামগুলি দিয়ে কিছু করা হতে পারে তাই আমার এক ঝলক রয়েছে। আইটেমসোর্সে বস্তুর বৈশিষ্ট্যগুলি দেখার জন্য এটি নেট। প্রতিবিম্ব ব্যবহার করে এবং প্রতিটিটির জন্য একটি কলাম তৈরি করে। সম্ভবত আমি প্রতিটি কলামের জন্য একটি সম্পত্তি সহ ফ্লাইতে একটি প্রকার উত্পন্ন করতে পারতাম তবে এটি ট্র্যাকের পথ ছাড়ছে।

এই সমস্যাটি কোডে এত সহজে ছড়িয়ে পড়েছে তাই যখনই নতুন কলামগুলির সাথে ডেটা প্রসঙ্গটি আপডেট করা হয় তখনই আমি একটি সাধারণ এক্সটেনশন পদ্ধতিটি সহ কল ​​করি call

public static void GenerateColumns(this DataGrid dataGrid, IEnumerable<ColumnSchema> columns)
{
    dataGrid.Columns.Clear();

    int index = 0;
    foreach (var column in columns)
    {
        dataGrid.Columns.Add(new DataGridTextColumn
        {
            Header = column.Name,
            Binding = new Binding(string.Format("[{0}]", index++))
        });
    }
}

// E.g. myGrid.GenerateColumns(schema);

1
সর্বোচ্চ ভোট এবং গ্রহণযোগ্য সমাধানটি সেরা নয়! দু 'বছর পরে উত্তর হবে: msmvps.com/blogs/deborahk/archive/2011/01/23/...
মিখাইল

4
না, এটা হবে না। প্রদত্ত লিঙ্কটি যাইহোক না, কারণ সেই সমাধানটির ফলাফলটি সম্পূর্ণ আলাদা!
321X

2
দেখে মনে হচ্ছে ম্লেলেকের সমাধানটি অনেক বেশি সর্বজনীন, এবং সি # কোডের সরাসরি ব্যবহার সমস্যাযুক্ত যেমন: কন্ট্রোলটেম্পলেটগুলিতে কার্যকর is
EFraim

@ মিখাইল লিঙ্কটি ভাঙ্গা হয়েছে
লাকলিলেকি

3
এখানে লিঙ্কটি দেওয়া হয়েছে: ব্লগস.এমএসএমভিপিএস
মিখাইল

9

ডেবোগ্রাহ কুরাতার একটি ব্লগ আর্টিকেলটি আমি পেয়েছি একটি দুর্দান্ত কৌশল সহ যে কীভাবে ডেটাগ্রিডে পরিবর্তনশীল সংখ্যক কলাম প্রদর্শন করা যায়:

এমভিভিএম ব্যবহার করে সিলভারলাইট অ্যাপ্লিকেশনটিতে গতিশীল কলামগুলির সাথে একটি ডেটাগ্রিড পপুলেট করা

মূলত, তিনি একটি তৈরি করে DataGridTemplateColumnএবং এতে ItemsControlএকাধিক কলাম প্রদর্শন করে inside


1
এটি এখন পর্যন্ত প্রোগ্রামযুক্ত সংস্করণ হিসাবে একই ফলাফল নয় !!
321X

1
@ ৩২১ এক্স: আপনি কি দয়া করে পর্যবেক্ষণ করা পার্থক্যগুলি ব্যাখ্যা করতে পারেন (এবং প্রোগ্রামযুক্ত সংস্করণ দ্বারা আপনি কী বোঝাতে চেয়েছেন , এটির সমস্ত সমাধান যেমন প্রোগ্রাম করা হয়) দয়া করে?
অথবা ম্যাপার



এটি আশ্চর্যর কিছু কম নয় !!
রবিদ গোল্ডেনবার্গ

6

আমি এ জাতীয় কোডের একটি লাইন ব্যবহার করে গতিশীলভাবে একটি কলাম যুক্ত করতে সক্ষম করেছিলাম:

MyItemsCollection.AddPropertyDescriptor(
    new DynamicPropertyDescriptor<User, int>("Age", x => x.Age));

প্রশ্নের সাথে সম্পর্কিত, এটি কোনও এক্সএএমএল-ভিত্তিক সমাধান নয় (যেহেতু উল্লিখিত হিসাবে এটি করার কোনও যুক্তিসঙ্গত উপায় নেই), তেমনি এটি এমন কোনও সমাধান নয় যা সরাসরি ডেটাগ্রিড.কলামগুলির সাথে কাজ করবে। এটি আসলে ডেটাগ্রিড বাউন্ড আইটেমসোর্স দিয়ে কাজ করে যা আইটি টাইপলিস্ট প্রয়োগ করে এবং যেমন সম্পত্তি-বিবরণী পুনরুদ্ধারের জন্য কাস্টম পদ্ধতি সরবরাহ করে। কোডে এক জায়গায় আপনি আপনার গ্রিডের জন্য "ডেটা সারি" এবং "ডেটা কলাম" সংজ্ঞায়িত করতে পারেন।

আপনি যদি:

IList<string> ColumnNames { get; set; }
//dict.key is column name, dict.value is value
Dictionary<string, string> Rows { get; set; }

আপনি উদাহরণস্বরূপ ব্যবহার করতে পারেন:

var descriptors= new List<PropertyDescriptor>();
//retrieve column name from preprepared list or retrieve from one of the items in dictionary
foreach(var columnName in ColumnNames)
    descriptors.Add(new DynamicPropertyDescriptor<Dictionary, string>(ColumnName, x => x[columnName]))
MyItemsCollection = new DynamicDataGridSource(Rows, descriptors) 

এবং আপনার গ্রিডটি মাই আইটেমস সংগ্রহের সাথে বাঁধার ব্যবহার করে সংশ্লিষ্ট কলামগুলির সাথে পপুলেশন হবে। এই কলামগুলি রানটাইম সময়ে গতিশীলভাবে সংশোধন করা যেতে পারে (নতুন যুক্ত বা বিদ্যমান সরানো) এবং গ্রিড স্বয়ংক্রিয়ভাবে এটির কলামগুলির সংগ্রহকে রিফ্রেশ করবে।

উপরে উল্লিখিত ডায়নামিকপ্রোপার্টিডিজিপেক্টর হ'ল নিয়মিত প্রোপার্টিডেস্কিটারে কেবলমাত্র একটি আপগ্রেড এবং কিছু অতিরিক্ত বিকল্পের সাথে দৃ strongly়ভাবে টাইপযুক্ত কলাম সংজ্ঞা সরবরাহ করে। ডায়নামিকডেটাগ্রিডসোর্স অন্যথায় বেসলিক সম্পত্তি-বিবরণীর সাথে সূক্ষ্ম ইভেন্টে কাজ করবে।


3

গৃহীত উত্তরের এমন একটি সংস্করণ তৈরি করুন যা সাবস্ক্রিপশন হ্যান্ডেল করে।

public class DataGridColumnsBehavior
{
    public static readonly DependencyProperty BindableColumnsProperty =
        DependencyProperty.RegisterAttached("BindableColumns",
                                            typeof(ObservableCollection<DataGridColumn>),
                                            typeof(DataGridColumnsBehavior),
                                            new UIPropertyMetadata(null, BindableColumnsPropertyChanged));

    /// <summary>Collection to store collection change handlers - to be able to unsubscribe later.</summary>
    private static readonly Dictionary<DataGrid, NotifyCollectionChangedEventHandler> _handlers;

    static DataGridColumnsBehavior()
    {
        _handlers = new Dictionary<DataGrid, NotifyCollectionChangedEventHandler>();
    }

    private static void BindableColumnsPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
    {
        DataGrid dataGrid = source as DataGrid;

        ObservableCollection<DataGridColumn> oldColumns = e.OldValue as ObservableCollection<DataGridColumn>;
        if (oldColumns != null)
        {
            // Remove all columns.
            dataGrid.Columns.Clear();

            // Unsubscribe from old collection.
            NotifyCollectionChangedEventHandler h;
            if (_handlers.TryGetValue(dataGrid, out h))
            {
                oldColumns.CollectionChanged -= h;
                _handlers.Remove(dataGrid);
            }
        }

        ObservableCollection<DataGridColumn> newColumns = e.NewValue as ObservableCollection<DataGridColumn>;
        dataGrid.Columns.Clear();
        if (newColumns != null)
        {
            // Add columns from this source.
            foreach (DataGridColumn column in newColumns)
                dataGrid.Columns.Add(column);

            // Subscribe to future changes.
            NotifyCollectionChangedEventHandler h = (_, ne) => OnCollectionChanged(ne, dataGrid);
            _handlers[dataGrid] = h;
            newColumns.CollectionChanged += h;
        }
    }

    static void OnCollectionChanged(NotifyCollectionChangedEventArgs ne, DataGrid dataGrid)
    {
        switch (ne.Action)
        {
            case NotifyCollectionChangedAction.Reset:
                dataGrid.Columns.Clear();
                foreach (DataGridColumn column in ne.NewItems)
                    dataGrid.Columns.Add(column);
                break;
            case NotifyCollectionChangedAction.Add:
                foreach (DataGridColumn column in ne.NewItems)
                    dataGrid.Columns.Add(column);
                break;
            case NotifyCollectionChangedAction.Move:
                dataGrid.Columns.Move(ne.OldStartingIndex, ne.NewStartingIndex);
                break;
            case NotifyCollectionChangedAction.Remove:
                foreach (DataGridColumn column in ne.OldItems)
                    dataGrid.Columns.Remove(column);
                break;
            case NotifyCollectionChangedAction.Replace:
                dataGrid.Columns[ne.NewStartingIndex] = ne.NewItems[0] as DataGridColumn;
                break;
        }
    }

    public static void SetBindableColumns(DependencyObject element, ObservableCollection<DataGridColumn> value)
    {
        element.SetValue(BindableColumnsProperty, value);
    }

    public static ObservableCollection<DataGridColumn> GetBindableColumns(DependencyObject element)
    {
        return (ObservableCollection<DataGridColumn>)element.GetValue(BindableColumnsProperty);
    }
}

2

আপনি গ্রিড সংজ্ঞা সহ একটি ব্যবহারকারীর নিয়ন্ত্রণ তৈরি করতে পারেন এবং এক্সএএমএল-এর বিবিধ কলাম সংজ্ঞা সহ 'চাইল্ড' নিয়ন্ত্রণগুলি সংজ্ঞায়িত করতে পারেন। পিতামাতার কলামগুলির জন্য নির্ভরতা সম্পত্তি এবং কলামগুলি লোড করার জন্য একটি পদ্ধতি প্রয়োজন:

মূল:


public ObservableCollection<DataGridColumn> gridColumns
{
  get
  {
    return (ObservableCollection<DataGridColumn>)GetValue(ColumnsProperty);
  }
  set
  {
    SetValue(ColumnsProperty, value);
  }
}
public static readonly DependencyProperty ColumnsProperty =
  DependencyProperty.Register("gridColumns",
  typeof(ObservableCollection<DataGridColumn>),
  typeof(parentControl),
  new PropertyMetadata(new ObservableCollection<DataGridColumn>()));

public void LoadGrid()
{
  if (gridColumns.Count > 0)
    myGrid.Columns.Clear();

  foreach (DataGridColumn c in gridColumns)
  {
    myGrid.Columns.Add(c);
  }
}

শিশু জ্যামল:


<local:parentControl x:Name="deGrid">           
  <local:parentControl.gridColumns>
    <toolkit:DataGridTextColumn Width="Auto" Header="1" Binding="{Binding Path=.}" />
    <toolkit:DataGridTextColumn Width="Auto" Header="2" Binding="{Binding Path=.}" />
  </local:parentControl.gridColumns>  
</local:parentControl>

এবং অবশেষে, জটিল অংশটি 'লোডগ্রিড' কোথায় কল করবে তা সন্ধান করছে।
আমি InitalizeComponentএটির সাথে লড়াই করছি তবে আমার উইন্ডো কনস্ট্রাক্টর (চাইল্ডগ্রিড হ'ল এক্স: উইন্ডো.এক্স্যামেলের নাম) এ ফোন করে কাজ করার জিনিসগুলি পেয়েছি :

childGrid.deGrid.LoadGrid();

সম্পর্কিত ব্লগ এন্ট্রি


1

আপনি অটো জেনারেট কলাম এবং একটি ডেটা টেম্পলেট দিয়ে এটি করতে সক্ষম হতে পারেন। আমি ইতিবাচক নই যদি এটি অনেক কাজ ছাড়াই কাজ করে, আপনি এটি নিয়ে কাজ করতে হবে। সত্যিই যদি আপনার যদি ইতিমধ্যে একটি কার্যক্ষম সমাধান হয় তবে আমি যদি এখনও কোনও বড় কারণ না পাই তবে আমি পরিবর্তনটি করতে পারবো না। ডেটাগ্রিড নিয়ন্ত্রণটি খুব ভাল হচ্ছে তবে এটি সহজে গতিশীল কাজ করতে সক্ষম হওয়ার জন্য এটি এখনও কিছু কাজ প্রয়োজন (এবং আমার অনেক কিছু শিখতে বাকি আছে)।


আমার কারণ হ'ল এএসপি থেকে আসা et নেট থেকে সজ্জিত ডেটা বাঁধাইয়ের মাধ্যমে আমি কী করতে পারি তা সম্পর্কে আমি নতুন এবং এটির সীমাটি কোথায় তা আমি নিশ্চিত নই। ধন্যবাদ, অটো জেনারেট কলামগুলি সহ আমার একটি নাটক হবে।
জেনেরিক ত্রুটি

0

আমি প্রোগ্রামগতভাবে যেভাবে করি তার একটি নমুনা রয়েছে:

public partial class UserControlWithComboBoxColumnDataGrid : UserControl
{
    private Dictionary<int, string> _Dictionary;
    private ObservableCollection<MyItem> _MyItems;
    public UserControlWithComboBoxColumnDataGrid() {
      _Dictionary = new Dictionary<int, string>();
      _Dictionary.Add(1,"A");
      _Dictionary.Add(2,"B");
      _MyItems = new ObservableCollection<MyItem>();
      dataGridMyItems.AutoGeneratingColumn += DataGridMyItems_AutoGeneratingColumn;
      dataGridMyItems.ItemsSource = _MyItems;

    }
private void DataGridMyItems_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
        {
            var desc = e.PropertyDescriptor as PropertyDescriptor;
            var att = desc.Attributes[typeof(ColumnNameAttribute)] as ColumnNameAttribute;
            if (att != null)
            {
                if (att.Name == "My Combobox Item") {
                    var comboBoxColumn =  new DataGridComboBoxColumn {
                        DisplayMemberPath = "Value",
                        SelectedValuePath = "Key",
                        ItemsSource = _ApprovalTypes,
                        SelectedValueBinding =  new Binding( "Bazinga"),   
                    };
                    e.Column = comboBoxColumn;
                }

            }
        }

}
public class MyItem {
    public string Name{get;set;}
    [ColumnName("My Combobox Item")]
    public int Bazinga {get;set;}
}

  public class ColumnNameAttribute : Attribute
    {
        public string Name { get; set; }
        public ColumnNameAttribute(string name) { Name = name; }
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.