Combining TreeView and Canvas in WPF ( MVVM )
-
I many projects I have had a similar type of problem that I have not found a good general solution to. Assuming that I have many different lines on a Canvas that are all organized in the flat storage Canvas.Children but I want these lines to be grouped by the object they are trying to draw. Like all lines in a Ball is grouped together and can be removed by a single checkbox click and at a higher level, I can remove the entire football field etc. The only way I found out to do this was through looping through everything every time I was adding, changing and removing it, basically involving recursion. Does anyone have some good ideas or practices for solving this?
-
I many projects I have had a similar type of problem that I have not found a good general solution to. Assuming that I have many different lines on a Canvas that are all organized in the flat storage Canvas.Children but I want these lines to be grouped by the object they are trying to draw. Like all lines in a Ball is grouped together and can be removed by a single checkbox click and at a higher level, I can remove the entire football field etc. The only way I found out to do this was through looping through everything every time I was adding, changing and removing it, basically involving recursion. Does anyone have some good ideas or practices for solving this?
I would normally base this type of thing on working with an underlying model using MVVM. Basically, I would have something like a Shape model which contained an ObservableCollection of Lines. All you would need to do then is remove the Shape and clear the Lines collection and the related items would be removed from the screen. Without seeing your code, I can't say whether or not this is feasible with the architecture you currently have in place.
This space for rent
-
I would normally base this type of thing on working with an underlying model using MVVM. Basically, I would have something like a Shape model which contained an ObservableCollection of Lines. All you would need to do then is remove the Shape and clear the Lines collection and the related items would be removed from the screen. Without seeing your code, I can't say whether or not this is feasible with the architecture you currently have in place.
This space for rent
At the lowest level, I have a class that inherits FrameWorkElement and use drawing visuals to actually render the objects. This is because I don't only have lines but Bezier curves etc. The problem was that I wanted to group things at an arbitrary deep levels MyDrawingFile -Road --Car ---Wheels ----Rims ----Tires ---Lights -----StopLights -----HeadLights ---Windows -House So if I wanted to disable everything but the House I basically had to traverse all the objects and its children under the ObservableCollection and then manually remove the objects in the Canvas. I basically set up something like:
public abstract class HierarchyBase : INotifyPropertyChanged
{private ObservableCollection children = new ObservableCollection(); public ObservableCollection Children { get { return children; } set { children = value; } } public string Name { get; set; } private bool pIsEnabled; public bool IsEnabled { get { return pIsEnabled; } set { pIsEnabled = value; StateUpdated(); OnPropertyChanged(); } } private bool pIsVisible; public bool IsVisible { get { return pIsVisible; } set { pIsVisible = value; StateUpdated(); OnPropertyChanged(); } } private void StateUpdated() { if (this is IDrawing) { ((IDrawing)this).Redraw(IsVisible); } foreach (HierarchyBase child in Children) { if (!this.IsEnabled || !this.IsVisible) { child.IsVisible = false; } } } public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged(\[CallerMemberName\]string caller = "") { // make sure only to call this if the value actually changes var handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(caller)); StateUpdated(); } } }
But then binding was a
-
I many projects I have had a similar type of problem that I have not found a good general solution to. Assuming that I have many different lines on a Canvas that are all organized in the flat storage Canvas.Children but I want these lines to be grouped by the object they are trying to draw. Like all lines in a Ball is grouped together and can be removed by a single checkbox click and at a higher level, I can remove the entire football field etc. The only way I found out to do this was through looping through everything every time I was adding, changing and removing it, basically involving recursion. Does anyone have some good ideas or practices for solving this?
-
List Ball = ... ... Canvas.Children.Add( line ); Ball.Add( line ); ... Ball.ForEach( l => Canvas.Children.Remove( l as UIElement ) );
"(I) am amazed to see myself here rather than there ... now rather than then". ― Blaise Pascal
Yes, something like this is what I tried to do, but I found it to be very messy. No binding and I have to resort to storing the connection to a canvas in the code behind. It does work but not really good MVVM?
-
Yes, something like this is what I tried to do, but I found it to be very messy. No binding and I have to resort to storing the connection to a canvas in the code behind. It does work but not really good MVVM?
-
It's a generic "infrastructure" helper "pattern"; nothing to do with "MVVM". Plumbing.
"(I) am amazed to see myself here rather than there ... now rather than then". ― Blaise Pascal
Not initially no. But as soon as I had the treeview up and running with binding:
This was very neat, but now how to bind the canvas to the code behind? I thought this was very messy.
public abstract class HierarchyBase : INotifyPropertyChanged
{
public HierarchyBase()
{
Delete = new RelayCommand( CanDelete, DeleteThis);
children.CollectionChanged +=ItemChildren_CollectionChanged;
}public Canvas myCanvas; private HierarchyBase \_parent; private void ItemChildren\_CollectionChanged(object sender,NotifyCollectionChangedEventArgs e) { //different kind of changes that may have occurred in collection if (e.Action == NotifyCollectionChangedAction.Add) { foreach (var item in e.NewItems) { ((HierarchyBase)item).\_parent = this; ((HierarchyBase)item).myCanvas = this.myCanvas; } //((List)e.NewItems).ForEach(i => ((HierarchyBase)i).\_parent = this); // foreach (e.NewItems)(i => i;) } if (e.Action == NotifyCollectionChangedAction.Replace) { //your code } if (e.Action == NotifyCollectionChangedAction.Remove) { foreach (var item in e.OldItems) { ((HierarchyBase)item).\_parent = null; ((HierarchyBase)item).myCanvas = null; } } if (e.Action == NotifyCollectionChangedAction.Move) { //your code } } private ObservableCollection children = new ObservableCollection(); public ObservableCollection Children { get { return children; } set { children = value; } } public ICommand Delete { get; set; } private bool CanDelete(object obj) { return IsEnabled; } private void DeleteThi
-
Not initially no. But as soon as I had the treeview up and running with binding:
This was very neat, but now how to bind the canvas to the code behind? I thought this was very messy.
public abstract class HierarchyBase : INotifyPropertyChanged
{
public HierarchyBase()
{
Delete = new RelayCommand( CanDelete, DeleteThis);
children.CollectionChanged +=ItemChildren_CollectionChanged;
}public Canvas myCanvas; private HierarchyBase \_parent; private void ItemChildren\_CollectionChanged(object sender,NotifyCollectionChangedEventArgs e) { //different kind of changes that may have occurred in collection if (e.Action == NotifyCollectionChangedAction.Add) { foreach (var item in e.NewItems) { ((HierarchyBase)item).\_parent = this; ((HierarchyBase)item).myCanvas = this.myCanvas; } //((List)e.NewItems).ForEach(i => ((HierarchyBase)i).\_parent = this); // foreach (e.NewItems)(i => i;) } if (e.Action == NotifyCollectionChangedAction.Replace) { //your code } if (e.Action == NotifyCollectionChangedAction.Remove) { foreach (var item in e.OldItems) { ((HierarchyBase)item).\_parent = null; ((HierarchyBase)item).myCanvas = null; } } if (e.Action == NotifyCollectionChangedAction.Move) { //your code } } private ObservableCollection children = new ObservableCollection(); public ObservableCollection Children { get { return children; } set { children = value; } } public ICommand Delete { get; set; } private bool CanDelete(object obj) { return IsEnabled; } private void DeleteThi
What we are doing is "shadowing"; we are just going about it in different ways depending on the circumstances. Your "hierarchyBase" is "view" support code; in particular, Canvas; infrastructure code. Not sure how your "tree" relates to your earlier "ball"; so I can't visualize or comment on it. Your architecture is "component based" (versus "view" based); or "views with components"; it should use appropriate patterns. I would consider integrating with Unity.
"(I) am amazed to see myself here rather than there ... now rather than then". ― Blaise Pascal