DevExpress TreeList DataSource Not Updating on UI - winforms

I have a TreeList that is inside a PopupContainerControl.
The TreeList is populated dynamically depending on the context in which the form is being used. This means that the Node List is updated and modified during runtime.
The problem I am having is that the TreeList is only showing the first modification made to the node structure, but not any following that one.
I am using a Custom Node class that implements IVirtualTreeListData. Shown Below.
public class Node<T> : TreeList.IVirtualTreeListData
{
public Node<T> Parent { get; set; }
public List<Node<T>> Children { get; set; }
public object[] Cells { get; set; }
public T Object { get; set; }
public Node(T t, Node<T> parent, object[] cells)
{
Parent = parent;
Children = new List<Node<T>>();
Cells = cells;
Object = t;
if (this.Parent != null)
this.Parent.Children.Add(this);
}
public Node(Node<T> parent, object[] cells)
{
Parent = parent;
Children = new List<Node<T>>();
Cells = cells;
if (this.Parent != null)
this.Parent.Children.Add(this);
}
public void VirtualTreeGetChildNodes(VirtualTreeGetChildNodesInfo info)
{
info.Children = Children;
}
public void VirtualTreeGetCellValue(VirtualTreeGetCellValueInfo info)
{
info.CellData = Cells[info.Column.AbsoluteIndex];
}
public void VirtualTreeSetCellValue(VirtualTreeSetCellValueInfo info)
{
Cells[info.Column.AbsoluteIndex] = info.NewCellData;
}
}
So when I first load the form, I make the following call.
_rootNode = new Node<MyResource>(null,null)
_treeList.DataSource = _rootNode;
Later, when the resources are loaded into the TreeList, it looks like the following:
Node<MyResource> newResourceNode = new Node<MyResource>(_rootNode,new object[]{"New Node"});
treeList.DataSource = _rootNode;
treeList.RefreshDataSource();
treeList.ExpandAll();
This will set the resources in the TreeList the FIRST time it is hit only, but not reflecting any following changes.

After change the datasource of the treelist, you can try:
treelist.EndUpdate();
You also need to keep the "BeginUpdate()" and "EndUpdate()" used same time, it will impact the "TreeList.IsLockUpdate" value,if you find "TreeList.IsLockUpdate" is "True",try to clear whether lack of "EndUpdate" in running time.

Related

Access treeviewitem from HierarchicalDataTemplate in WPF

I am trying to access the treeviewitem created using HierarchicalDataTemplate based on the Name of the header. Also i want to access the control inside (in this case rectangle) the treeviewitem and change its color. I tried many ways but no success. Below is my code. I am generating Treeview using custom class and xml.
public partial class Window1 : Window
{
ObservableCollection<Step> TreeViewTemplate;
public Window1()
{
TreeViewTemplate = new ObservableCollection<Step>();
InitializeComponent();
SetDataTemplate("NEWSITECOPPER_PROPOSAL", "Proposal");
tvMain.ItemsSource = TreeViewTemplate;
getTreeViewItem();
}
private void getTreeViewItem()
{
TreeViewItem item = (TreeViewItem)(tvMain.ItemContainerGenerator.ContainerFromItem(tvMain.Items[3]));
}
private void SetDataTemplate(string ProcessName, string journeyName)
{
try
{
TreeViewTemplate.Clear();
//XDocument xDoc = XDocument.Load(#"C:\Users\606347769\Desktop\Hemil\Others\TreeView\TreeView\Data.xml");
XDocument xDoc = XDocument.Load(#"C:\Users\606347769\Documents\Visual Studio 2008\Projects\TestAPplication\WpfApplication1\ProcessJourneyCriteria.xml");
var JourneySteps = xDoc.Elements("ProcessAreas").Elements("Process").Where(x =>
x.Attribute("name").Value == ProcessName).Select(y =>
y.Elements("Journey").Where(k => k.Attribute("name").Value == journeyName));
var FinalSteps = JourneySteps.FirstOrDefault();
FinalSteps.Elements("Step").ToList<XElement>().ForEach(x =>
{
string key = x.Attribute("name").Value;
ObservableCollection<ChildStep> value = new ObservableCollection<ChildStep>();
x.Elements("ChildStep").ToList<XElement>().ForEach(y =>
{
ObservableCollection<GrandChildStep> GC = new ObservableCollection<GrandChildStep>();
y.Elements("GrandChildStep").ToList<XElement>().ForEach(k =>
{
GC.Add(new GrandChildStep { Name = k.Attribute("name").Value });
});
value.Add(new ChildStep { Name = y.Attribute("name").Value, GrandChildStep = GC });
});
TreeViewTemplate.Add(new Step { Name = key, ChildStep = value });
});
}
catch (Exception)
{
}
}
}
Below is the custom class i have created
class Step
{
public string Name { get; set; }
public System.Collections.ObjectModel.ObservableCollection<ChildStep> ChildStep { get; set; }
}
class ChildStep
{
public string Name { get; set; }
public System.Collections.ObjectModel.ObservableCollection<GrandChildStep> GrandChildStep { get; set; }
}
class GrandChildStep
{
public string Name { get; set; }
}
You should expose everything you want to access on your (view) model and just bind to it, like the Background of the shape or the IsSelected property of the item (needs to be bound in ItemContainerStyle).
If you need to "access UI controls" in WPF you are usually doing something wrong.
name the child,for eg. in your case x:Name="Rect" to Rectangle
then
Declare this helper method in your Code
T GetVisualChild<t>(DependencyObject parent, string name) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<t>(v, name);
}
if (child != null)
{
break;
}
}
return child;
}
then
just declare Rectangle Rect=new Rectangle(); in constructor or loaded event.
and when you want to access the child. use that declared helper method. for eg.
Rect=GetVisualChild<treeview>(this, "Rect")
Note: here "treeview" is the name of parent You may give the name of parent accessing the child directly.

Binding hierarchical data with templates using a generic Tree data structure

How to bind a Tree data structure using hierarchical data template to a tree list control. The difference is that instead of creating different types for each level in hierarchy I intend to use only one type “Node” differentiated by the “Type” enumeration indicating its level in the hierarchy. Is this a feasible approach. How to display the data using TreeListControl.
public class TreeNode<T> where T : new()
{
public TreeNode<T> Parent { get; set; }
public IList<TreeNode<T>> Children { get; set; }
protected TreeNodeType Type { get; set; }
public T Current { get; set; }
public TreeNode()
{
}
public TreeNode(TreeNodeType type)
{
this.Type = type;
this.Current = new T();
this.Children = new List<TreeNode<T>>();
}
public void AddChildren(TreeNode<T> child)
{
child.Parent = this;
this.Children.Add(child);
}
public override string ToString()
{
return string.Format("Type :{0} Name :{1}", this.Type, this.Current);
}
}
/// <summary>
/// Tree node type
/// </summary>
public enum TreeNodeType
{
Manager = 0,
Employee,
}
public class EmployeeNode
{
public string Name { get; set; }
public override string ToString()
{
return this.Name;
}
}
You're going to need to use a DataTemplateSelector. MSDN describes how to use one. Even though the example is for a ListBox, you can do this with a TreeView.
I created a data selector that always returns the same template. This will return the same template for each node of the self referencing data.
public class HierarchialDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
FrameworkElement element = container as FrameworkElement;
if (element != null && item != null)
{
return element.FindResource("HierarchialDataTemplate") as DataTemplate;
}
return null;
}
}

MVVM: how to create a ViewModel from Model object

I want to get values of my model and create a viewmode
In my Model I have
public class TestElement
{
public TestElement CurrentNode { get; set; }
public TestElement Parent { get; set; }
}
I have some method that do this
if (thisNode == null)
{
thisNode = new TestElement { Name = name, Parent = CurrentNode };
currentCollection.Add(thisNode);
}
In my view model I want to create TestElementViewModel Parent and get my model Parent values
public class TestElementViewModel
{
public TestElementViewModel Parent { get; set; }
I want to use it in this method
public IEnumerable<TestElementViewModel> ToTreeViewModel(IEnumerable<TestElement> treemodel)
{
foreach (TestElementitem in treemodel)
yield return new TestElementViewModel
{
Id = item.Id,
Name = item.Name,
Children = ToTreeViewModel(item.Children).ToList(),
Parent = item.Parent
};
}
}
How can I achieve that?
I'm guessing your casting error occurs on the the line
Parent = item.Parent
Well the Parent property in your TestElementViewModel isn't a TestElement type so you can't do that.
Try assigning a new TestElementViewModel instead.
Parent = new TestElementViewModel { Id = item.Parent.Id, Name = item.Parent.Name, ... }
One improvement you might want to consider is using wrappers in your ViewModel class, which will make assigning properties a little easier.
For example,
public class TestElementViewModel : INotifyPropertyChanged
{
public TestElementViewModel(TestElement model)
{
Model = model;
if(Model.Parent != null)
Parent = new TestElementViewModel(Model.Parent);
}
public TestElement Model { get; private set; }
private TestElementViewModel _parent;
public TestElementViewModel Parent
{ get { return _parent; }
set { _parent = value; OnPropertyChanged("Parent"); }
}
public int Id
{
get { return Model.Id; }
set { Model.Id = value; OnPropertyChanged("Id"); }
}
// rest of the properties need wrapping too
}
makes it so that you don't have to manually assign the properties each time you instantiate a new viewmodel.

DomainContext sometimes still HasChanges after SubmitChanges completes

I have a very simple server model that includes a parent entity with a [Composition] list of child entities. In my client, I have 2 functions. One function removes all the child entities from the parent and the other removes all and also edits a property on the parent entity.
When I simply remove all child entities and SubmitChanges(), all is well.
When I remove all child entities and edit the parent and SubmitChanges(), there are still pending changes (HasChanges == true) when the SubmitChanges() callback is fired.
I am using Silveright 4 RTM and RIA Services 1.0 RTM.
Any ideas what is going on here?
Here are the server entities:
public class RegionDto
{
public RegionDto()
{
Cities = new List<CityDto>();
}
[Key]
public int Id { get; set; }
public string Name { get; set; }
[Include]
[Composition]
[Association("RegionDto_CityDto", "Id", "RegionId")]
public List<CityDto> Cities { get; set; }
}
public class CityDto
{
[Key]
public int Id { get; set; }
public int RegionId { get; set; }
public string Name { get; set; }
}
And here is the client code:
public static class CState
{
private static RegionDomainContext _domainContext;
public static RegionDomainContext DomainContext
{
get
{
if (_domainContext == null)
{
_domainContext = new RegionDomainContext();
}
return _domainContext;
}
}
public static void SaveChanges()
{
DomainContext.SubmitChanges(op =>
{
if (DomainContext.HasChanges && !DomainContext.IsSubmitting)
{
var w = new ChildWindow();
w.Content = "The DomainContext still has unsaved changes.";
w.Show();
}
}, null);
}
}
public partial class MainPage : UserControl
{
private void ClearCitiesEditRegion(object sender, RoutedEventArgs e)
{
var region = (RegionDto)regionList.SelectedItem;
if (region != null)
{
region.Name += "*";
while (region.Cities.Count > 0)
{
region.Cities.Remove(region.Cities.First());
}
CState.SaveChanges();
}
}
private void ClearCities(object sender, RoutedEventArgs e)
{
var region = (RegionDto)regionList.SelectedItem;
if (region != null)
{
while (region.Cities.Count > 0)
{
region.Cities.Remove(region.Cities.First());
}
CState.SaveChanges();
}
}
}
When you run this code the ChildWindow is only shown when you the ClearCitiesEditRegion() method is called. The only difference between this and the ClearCities() method is the line where I edit the region.Name property.
You can also download a sample project that reproduces this here: http://dl.dropbox.com/u/2393192/RIA_Services_Problem.zip
I received an answer to this on the Silverlight forums. Apparently this is a bug in RIA Service 1.0. The following is Matthew's response on the Silverlight forums.
Yes, I've confirmed this is a bug.
Thanks for reporting it and providing
the repro. As you discovered, the bug
will only repro in composition
scenarios where the parent has been
modified in addition to one or more
children. The workaround is to do an
explicit AcceptChanges if the submit
was successful. For example, here is
the code you'd write in a submit
callback:
if (!submitOperation.HasError)
{
((IChangeTracking)ctxt.EntityContainer).AcceptChanges();
}
This will accept all changes and reset
change state correctly.

DataGridViewColumn.DataPropertyName Property

I have a DataGridView control and I want to populate it with data.
I use DataSource property
// dgvDealAsset is DataGridView
private void DealAssetListControl_Load(object sender, EventArgs e)
{
dgvDealAssets.AutoGenerateColumns = false;
dgvDealAssets.DataSource = DealAssetList.Instance.Values.ToList();
}
Now problem number one. The class of my collection does not contain only simple types that I can map to columns using DataPropertyName. This is the class that is contained in collection.
class MyClass
{
public String Name;
MyOtherClass otherclass;
}
class MyOtherClass
{
public String Name;
}
Now I am binding properties of MyClass to columns
col1.DataPropertyName = "Name" // Ok
col2.DataPropertyName = "otherclass" // Not OK - I will have empty cell
The problem is that I want to display otherclass.Name field. But if I try to write
col2.DataPropertyName = "otherclass.Name"
I get empty cell.
I tried to manually set the column
private void DealAssetListControl_Load(object sender, EventArgs e)
{
dgvDealAssets.AutoGenerateColumns = false;
dgvDealAssets.DataSource = DealAssetList.Instance.Values.ToList();
// iterate through rows and set the column manually
foreach (DataGridViewRow row in dgvDealAssets.Rows)
{
row.Cells["Column2"].Value = ((DealAsset)row.DataBoundItem).otherclass.Name;
}
But this foreach cycle takes about minute to complete (2k elements). How to solve this problem?
DataGridView doesn't support databinding to child properties. For more info, check this post
I like the solution that uses the CellFormatting event.
Problem nr.1:
Try to do the following:
extend MyOtherClass from Object (this step might not be needed)
and override, or create, method ToString().
That should do it.
In case you want to use many child elements like this:
class MyClass
{
public int Id;
public MyOtherClass OtherClass;
}
class MyOtherClass
{
public string Name;
public int Number;
}
How about
1st solution
Set value for each cell in some event (mabye other one is better), manually, after setting datasource, for example:
private void dgv_CellFormatting( object sender, DataGridViewCellFormattingEventArgs e )
{
MyClass data = dgv.Rows[ e.RowIndex ].DataBoundItem as MyClass;
dgv.Rows[ e.RowIndex ].Cells[ "colName" ].Value = data.OtherClass.Name;
dgv.Rows[ e.RowIndex ].Cells[ "colNumber" ].Value = data.OtherClass.Number;
}
2nd solution
What about creating a DataTable from the data and then bind it?
I'd be thankful for any opinion ;-)
It sounds like the DataGridView's virtual mode would solve your problem. In virtual mode, the DataGridView will fire an event whenever it needs to display a cell. The event lets you populate the cell however you please. The advantage of virtual mode is the system only needs to pull the data that's actually being displayed, so there's no slow start-up time while you load everything.
private void my_init_function() {
datagridview.VirtualMode = true;
datagridview.CellValueNeeded += new System.Windows.Forms.DataGridViewCellValueEventHandler(datagridview_CellValueNeeded);
}
private void datagridview_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
{
e.Value = get_my_data(e.RowIndex, e.ColumnIndex);
}
The way of databinding a specific column of a datagrid to a child property of the datagrid's datasource is using the DataGridView.Column.Tag property, along with the ToString() override method inside the child object. It goes as follows:
public class Car
{
public int Id { get; set; }
public int Name { get; set; }
public string Colour { get; set; }
public Wheel Wheel { get; set; }
}
public class Wheel
{
public string WheelName { get; set; }
public override string ToString()
{
return WheelName;
}
}
public class Main
{
private void LoadGrid(List<Car> data)
{
this.dataGridView.Columns["Wheel"].Tag = "WheelName";
}
}

Resources