简体   繁体   English

(WPF)XamDataGrid +分层+按需加载+动态列

[英](WPF) XamDataGrid + Hierarchical + Load on Demand + Dynamic Columns

I am using XamDataGrid and need each row to be hierarchical (with several child nodes that are also hierarchical). 我正在使用XamDataGrid并且需要每一行都是分层的(有几个子节点也是分层的)。 It must load its data on demand and have dynamic child columns based on the results that are returned by a server I'm querying. 它必须按需加载其数据,并根据我正在查询的服务器返回的结果具有动态子列。

Fast forwarding a bit: 快进一点:

  • I have gone down the road of using ITypedList and ICustomPropertyDescriptor to dynamically add/remove properties so that the corresponding columns can be manipulated. 我已经开始使用ITypedList和ICustomPropertyDescriptor来动态添加/删除属性,以便可以操作相应的列。 To no avail. 无济于事。

  • I have tried dynamically changing FieldLayouts in the code behind, for each specific level of hierarchy, specified by ParentFieldLayoutKey. 我尝试在后面的代码中动态更改FieldLayouts,用于由ParentFieldLayoutKey指定的每个特定层次结构。 Yet, if I modify ANY of my field layouts, it applies the changes to all field layouts visually, but in the code behind, only the chosen field layout is actually modified. 但是,如果我修改了我的任何字段布局,它会直观地将更改应用于所有字段布局,但在后面的代码中,实际上只修改了所选的字段布局。 Why can't I modify one FieldLayout without changing all of them? 为什么我不能在不改变所有FieldLayout的情况下修改它们?

  • I have also tried using a DataSet that contains tables with relationships to ESTABLISH the desired hierarchy, but in doing so have thus far been unable to load data on demand. 我还尝试使用包含具有关系的表的DataSet来建立所需的层次结构,但是这样做到目前为止还无法按需加载数据。 Unless there's some way to do that, that I haven't been able to find anywhere in the infragistics docs? 除非有某种方法可以做到这一点,否则我无法在infragistics docs中的任何地方找到它?

Here are my requirements: 这是我的要求:

  • Must be hierarchical 必须是分层的
  • Must load data on demand (on expand) 必须按需加载数据(在扩展时)
  • Columns should not be known fully until runtime/on expand/when server is queried. 在运行时/展开/查询服务器之前,不应完全知道列。

Questions: 问题:

  • Is it possible to achieve all of these things with XamDataGrid? 是否可以使用XamDataGrid实现所有这些功能?

Edit: Yep. 编辑:是的。 It's possible. 这是可能的。

Here is my version. 这是我的版本。 This basically does what you were looking for. 这基本上可以满足您的需求。 In the child fieldlayout columns are dynamically added and data binding is performed accordingly 在子fieldlayout中,动态添加列并相应地执行数据绑定

Models: 楷模:

public class Country
{
    private string _name;
    public string Name
    {
        get { return this._name; }
        set { this._name = value; }
    }
    public ObservableCollection<State> States { get; set; }
}

public class State
{
    private readonly Dictionary<string, object> _info = new Dictionary<string, object>();

    /// <summary>
    /// Attributes of the state can be added dynamically (key will be the attribtue name etc...)
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public object this[string key]
    {
        get
        {
            return this._info[key];
        }
        set
        {
            this._info[key] = value;
        }
    }

    public string StateName { get; set; }
}

Behaviour: 行为:

public class GridFieldLayoutBehaviour : Behavior<XamDataGrid>
{
    protected override void OnAttached()
    {
        base.OnAttached();

        this.AssociatedObject.FieldLayoutInitialized += OnFieldLayoutInitialized;            
        this.AssociatedObject.RecordExpanded += OnRecordExpanded;
    }

    void OnRecordExpanded(object sender, Infragistics.Windows.DataPresenter.Events.RecordExpandedEventArgs e)
    {
        ((ViewModel)this.AssociatedObject.DataContext).AddStateAttributes();
    }

    void OnFieldLayoutInitialized(object sender, Infragistics.Windows.DataPresenter.Events.FieldLayoutInitializedEventArgs e)
    {
        if( e.FieldLayout.ParentFieldName == "States")
        {
            ((ViewModel)this.AssociatedObject.DataContext).GridFieldLayout = e.FieldLayout;
        }            
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();            
        this.AssociatedObject.FieldLayoutInitialized -= OnFieldLayoutInitialized;
        this.AssociatedObject.RecordExpanded -= OnRecordExpanded;
    }
}

ViewModel: 视图模型:

public class ViewModel
{
    #region Private Fields
    private readonly ObservableCollection<Country> _countries = new ObservableCollection<Country>();        
    private readonly List<string> _stateTotalAttributes = new List<string>();        
    #endregion

    public ViewModel()
    {
        FillData();

        var stateAttributes = new string[] {"Population", "Unemployment Rate", "Capital", "Governor", "TimeZone", "Area"};
        foreach (var stateAttribute in stateAttributes)
            _stateTotalAttributes.Add(stateAttribute);                            
    }

    public ObservableCollection<Country> Countries
    {
        get { return this._countries; }
    }

    public FieldLayout GridFieldLayout { get; set; }

    /// <summary>
    /// Country and the states are populated
    /// </summary>
    private void FillData()
    {
        var country = new Country();
        country.States = new ObservableCollection<State>();            
        country.Name = "USA";            

        var xdoc = XDocument.Load("states_data.xml");
        foreach (var state in xdoc.Descendants("states").Descendants())
        {
            var newState = new State();
            newState.StateName = state.Attributes("name").FirstOrDefault().Value;
            newState["Unemployment Rate"] = state.Attributes("unemployment-rate").FirstOrDefault().Value;
            newState["Capital"] = state.Attributes("capital").FirstOrDefault().Value;
            newState["Governor"] = state.Attributes("governor").FirstOrDefault().Value;
            newState["Area"] = state.Attributes("area").FirstOrDefault().Value;
            newState["TimeZone"] = state.Attributes("timezone").FirstOrDefault().Value;
            newState["Population"] = state.Attributes("population").FirstOrDefault().Value;

            country.States.Add(newState);
        }

        _countries.Add(country);
    }

    public void AddStateAttributes()
    {
        GridFieldLayout.Fields.BeginUpdate();

        // Remove the current columns (except for StateName)
        var removableFields = GridFieldLayout.Fields.Where(f => f.Name != "StateName");
        removableFields.ToList().ForEach(field => GridFieldLayout.Fields.Remove(field));

        // Figure out what state attributes to add
        var random = new Random(DateTime.Now.Millisecond);
        var numCols = random.Next(1, 6);
        var colsToAdd = GetStateAttributes(numCols, random);

        // Finally add the new ones'
        foreach (var col in colsToAdd)
        {
            var field = new UnboundField();
            field.Name = col;
            field.Binding = new Binding()
            {
                Mode = BindingMode.TwoWay,
                Path = new PropertyPath(string.Format("[{0}]",  col)),
                UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
            };

            GridFieldLayout.Fields.Add(field);
        }
        GridFieldLayout.Fields.EndUpdate();
    }

    private List<string> GetStateAttributes(int numCols, Random random)
    {            
        List<string> colsToAdd = new List<string>();
        for( int i = 0; i < numCols; i++)
        {
            int idx = random.Next(1, 6) - 1;
            if(colsToAdd.Contains(_stateTotalAttributes[idx]) == false)
            {
                colsToAdd.Add(_stateTotalAttributes[idx]);
            }
        }
        return colsToAdd;
    }
}

XAML: XAML:

    <igDP:XamDataGrid DataSource="{Binding Countries}" >
        <i:Interaction.Behaviors>
            <local:GridFieldLayoutBehaviour/>
        </i:Interaction.Behaviors>
    </igDP:XamDataGrid>

States_Data.xml: States_Data.xml:

<states>
  <state name="New York" population="19,651,127" unemployment-rate="" capital="Albany" governor="Andrew Cuomo" timezone="EST" area="54,556 sq mi"></state>
  <state name="New Hampshire" population="1,323,459 " unemployment-rate="" capital="Concord" governor="Maggie Hassan" timezone="EST" area="9,304 sq mi"></state>
</states>

Regards, Vishwa 此致,Vishwa

Figured it out. 弄清楚了。

Since I can't paste the code (technically, it belongs to the company I work for), I guess I'll just have to settle with explaining BASICALLY what I did. 由于我无法粘贴代码(从技术上讲,它属于我工作的公司),我想我只需要解决基本上解释我做了什么。

I made two wrappers, one to wrap my object to expose/create hierarchy (AutoGeneratedColumns="True", by default, assumes that a collection of objects inside of your object is your object's CHILDREN), and an ITypedList to wrap the new wrapper, so you can dynamically add properties to it. 我创建了两个包装器,一个用于包装我的对象以暴露/创建层次结构(AutoGeneratedColumns =“True”,默认情况下,假设对象内部的对象集合是对象的CHILDREN),以及用于包装新包装器的ITypedList,所以你可以动态添加属性。

I hope this post was at least somewhat informative. 我希望这篇文章至少可以提供一些信息。

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

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