I have a simple POCO data structure with what I believe is the correct implementation of loading a list of children in a 'Lazy' manner.
Something like this:
public class Parents
{
public Guid ID { get; set; }
public string Name { get; set; }
//children
private List<Children> childrenList;
public List<Children> ChildrenList
{
get
{
if (childrenList == null) { childrenList = Children.FindByParentID(ID); }
return childrenList;
}
set childrenList = value;
}
}
I need to use a DevExpress XtraGrid control to present the data as it is the control set used by my employer.
I bind the List<Parents> to the grid (e.g. myGrid.DataSource = Parents.FindAll(); ) and everything is fine until I try to expand the first child list using the "+" expansion indicator on the grid.
After I try to expand the first item, the grid (I'm assuming) goes through every parent and tries to load the child list resulting in very slow performance. I can see this while debugging.
I would like the grid to only load the child list for the single item requested. Any ideas about how to achieve this behavior would be very much appreciated.
Related
I have a small caliburn micro MVVM project with a DataGrid. The columns will consist of x amount of 'setup's and the rows will consist of 'CustomRow'. I would like to use a ObservableCollection where CustomRow has a function property and a collection of setup property. For each setup in this collection a column should exist with the value of setup.
class CustomRow
{
public string Function { get; set; }
public ObservableCollection<Setup> Setups { get; set; }
}
// example class
class Setup
{
public string Name { get; set; }
public object Content { get; set; }
}
So I need to be able to add columns and rows dynamically depending on the itemssource (all the Setups collections will have the same size).
My problem is that I don't know how to translate the Setups property into multiple columns.
I have spend quit some time on what should be a mundane problem in my opinion. But I am missing something.
Any help is much appreciated.
Bind or set the ItemsSource to the ObservableCollection<CustomRow> and then get the Setups property of the first CustomRow in the source collection in the view, e.g.:
private void Window_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var vm = DataContext as YourViewModel;
dataGrid.Columns.Clear();
if (vm.Rows != null && vm.Rows.Count > 0)
{
var setups = vm.Rows[0].Setups;
foreach (var setup in setups)
{
dataGrid.Columns.Add(new DataGridTextColumn { Header = setup.Name, Binding = new Binding("Content ") });
}
}
dataGrid.ItemsSource = vm.Rows;
}
There is no way to bind the Columns property of the DataGrid directly to a source property so you need to create the columns yourself one way or another.
You should do this in the view, in the control or in an attached behaviour that is attached to either of them. A view model should not create any columns.
Need help binding list of lists in xaml.
Have already tried to implement the same using Nested Listview but that's making my app crash when I scroll. I am guessing that's not a good idea.
Here is my Parent Model Class:
public class Parent
{
public string Id { get; set; }
public string Name { get; set; }
public List<Child> ChildList { get; set; }
}
Child Class:
public class Child
{
public string ChildName{ get; set; }
}
ViewModel:
public ObservableCollection< Parent > MyList = new ObservableCollection< Parent >();
In Mylist, I get a list of "Parent" objects, each containing the Id, Name and the 'ChildList' object which is also a list (multiple ChildList objects), containing ChildName
In my listview with Itemsource as "MyList", I want "Name" as the Header that contains the "ChildList" (Multiple "ChildName"s)
Something like:
Name1
-ChildName1
-ChildName2
-ChildName3
Name2
-ChildName1
-ChildName2
Can I achieve the same using listview grouping? If so, can someone please help me with the implementation as I am not able to group the data properly..
I think you might need to take another look at your design. If I personally was trying to do something like what I understand you are trying to do, I would handle one of these two ways.
The first way, I would start off with a listview of parents. I would then handle the clicked/tapped event (depending of what platform you are deving for) to open a second, smaller listview over the main page displaying the children of the parent that was clicked/tapped. Clicking/tapping on anywhere on the screen other than the child listview would put it away.
The second way I can think of doing it is simply adding a parent to the listview and then adding all of it's children. Something list this:
void FillParentListView(List<Parent> parents)
{
foreach(Parent p in parents)
{
listview.Items.Add(p);
foreach(Child c in p.ChildList)
listview.Items.Add(c);
}
}
Downside with that second option is you would probably need to create different cell designs to make it easy to distinguish between a parent and child, and you would need to try cast every time you want to read info from the listview to see what's been selected.
Given data structure that follows this pattern
class Top
{
string Header { get; set; }
ObservableCollection<Middle> Children { get; set; }
}
class Middle
{
string Header { get; set; }
ObservableCollection<Bottom> Children { get; set; }
}
class Bottom
{
string Header { get; set; }
bool IsSelected { get; set; }
}
If Top contains two different instances of Middle and both of those contain the same instance of Bottom, how can I determine the parent of the selected item? I'd like to remove the Bottom from the selected Middle, but not both.
The expected behavior when the items are linked is that changes to either one will always update the other. Referencing the same instance in both collections creates the intended behavior in the TreeView right now.
Can the visual tree helper be used from the ViewModel for this purpose?
how can I determine the parent of the selected item?
May be by applying the Parent property in it? But then you won't be able to have the same instance of Bottom class since their parents will be different.
class Bottom
{
Middle Parent { get; set; }
string Header { get; set; }
bool IsSelected { get; set; }
}
I would recommend you to have a separate ViewModel with the Parent property for every TreeView item in the TreeView. Then you could inject the same instance of BottomModel into separate BottomViewModels (which have their corresponding parents). In this case both BottomViewModels will expose the same data from BottomModel but you could remove them independently.
There is another (probably clearer and more proper) approach where you can have different instances of Bottom class (whatever Model or ViewModel) with Parent property and you keep them linked and in sync with something like Event Aggregator.
Also, here is a pretty comprehensive tutorial how to work with a TreeView in WPF/MVVM:
https://www.codeproject.com/Articles/26288/Simplifying-the-WPF-TreeView-by-Using-the-ViewMode
Using the helper class in this blog
https://rachel53461.wordpress.com/2011/10/09/navigating-wpfs-visual-tree/
/// <summary>
/// Returns the first ancester of specified type
/// </summary>
public static T FindAncestor<T>(DependencyObject current)
where T : DependencyObject
{
current = VisualTreeHelper.GetParent(current);
while (current != null)
{
if (current is T)
{
return (T)current;
}
current = VisualTreeHelper.GetParent(current);
};
return null;
}
Then in the DataTemplate PreviewMouseRightButtonUp and PreviewMouseLeftButtonUp for the content control and add the following code
var current = VisualTreeHelpers.FindAncestor<TreeViewItem>(sender as UIElement);
var parent = VisualTreeHelpers.FindAncestor<TreeViewItem>(current);
var parentVM = parent.DataContext as ITreeNode;
(DataContext as NavigationViewModel).SelectedParent = parentVM;
ITreeNode defines a method RemoveChild(ITreeNode) and all of the objects in my tree implement ITreeNode.
When removing an item call MainViewModel.inst.Navigation.RemoveChild(child);
The key to this solution is that the code behind is able to differentiate between TreeViewItems that are created by the same instance of Bottom.
I want to have a container of objects that will be visualised in WinForms' DataGridView control.
As far as I know, I can bind a container to DataGridView via the DataSource property.
But I wonder, is there any way to do it in Designer? I see a lot of options related to that (like DataSource, DataMember etc) but I don't see any option that will give me a possibility to choose my own container inside the Form class.
I don't want to make this kind of stuff programmatically if it's available in the designer.
DataSource is bindable in designer. You can press the little arrow at the top of the DataGridView, and Choose Data Source. At the bottom> Add Project DataSource...
After adding the class, that will contain Your DataSource, You can set in the properties pane which public property is Your DataSource.
For example:
public class ThereIsDataSourceInThisClass
{
public ThereIsDataSourceInThisClass()
{
MyDataSource = new BindingList<Thing>();
MyDataSource.Add(new Thing { First = "aa", Second = "bb" });
}
public BindingList<Thing> MyDataSource { get; set; }
public class Thing
{
public string First { get; set; }
public string Second { get; set; }
}
}
I selected ThereIsDataSourceInThisClass in the wizard, than I selected MyDataSource in the DataSource property.
The result is>
I have looked at a lot of places and I'm struggling to do this supposedly simple thing. I have a Windows form on which I've to show a simple DataGridView in this form:
| (CheckBoxColumn) | FilePath | FileState |
Now, there are a couple of problems. The data I need to bind to them is in a List of objects like this:
class FileObject
{
string filePath;
string fileState;
}
//Now here's the list of these objects which I populate somehow.
List<FileObject> listFiles;
Is there any efficient way to bind this directly to the DataGridView
so that different members of Object in the list are bound to
different columns, and for each there's checkbox?
I saw the examples of adding checkbox column to a datagrid, but none of them showed how
to add it to the 'header' row as well, so that it can behave as a 'check all'/'uncheck all' box.
Any help in how to achieve this would be great! I do write simple applications in C# but never had to work with datagrids etc. :(
Thanks!
The DataGridView control has a feature to automatically generate columns that can be set by the AutoGenerateColumns property. This property defaults to true - that is columns are by default auto generated.
However, columns are only automatically generated for public properties of the object you bind to the grid - fields do not show up.
Auto generation also works for check box columns when there is a public boolean property on the bound object.
So the simplest way to achieve your first two requirements is to change your FileObject class to this:
public class FileObject
{
public string FilePath { get; set; }
public string FileState { get; set; }
public bool Selected { get; set; }
}
If you cannot modify that class then next best would be the create a wrapper object that holds a file object:
public class FileObjectWrapper
{
private FileObject fileObject_;
FileObjectWrapper()
{
fileObject_ = new FileObject();
}
FileObjectWrapper(FileObject fo)
{
fileObject_ = fo;
}
public string FilePath
{
get { return fileObject_.filePath; }
set { fileObject_.filePath = value; }
}
public string FileState
{
get { return fileObject_.fileState; }
set { fileObject_.fileState= value; }
}
public bool Selected { get; set; }
}
You can then create your list to bind to (a BindingList is usually best) doing something like:
var fowList = new BindingList<FileObjectWrapper>();
foreach (FileObject fo in // here you have your list of file objects! )
{
fowList.Add(new FileObjectWrapper(fo));
}
dataGridView1.DataSource = fowList;
There are many ways to do the above but that is a general idea.
You can also add an unbound DataGridViewCheckBoxColumn to the grid, though I find it easier to have in the the bound list. Here is how if you do need to:
DataGridViewCheckBoxColumn c = new DataGridViewCheckBoxColumn();
c.Name = "Selected";
dataGridView1.Columns.Add(c);
Finally, for having a "SelectedAll" option in the header you will need to use custom code.
The article on CodeProject that Umesh linked to (CheckBox Header Column for DataGridView) looks quite easy to implement - they create a custom DataGridViewHeaderCell overriding the Paint and OnMouseClick methods.
Please refer the the below example, showing exactly what you are looking for
http://www.codeproject.com/Articles/20165/CheckBox-Header-Column-For-DataGridView