WPF Control Puzzle - wpf

I am new to WPF and am wondering how to best achieve a master detail grid as shown below.
The user will be able to press the right/left arrow keys to open/close Parents or click on the icon to achieve the same result. The data structure will be a parent/child 1 level deep.
How would I go about this?
-------------------------------------
Parent1 (P1) ^
-------------------------------------
P1 - Child 1
-------------------------------------
P1 - Child 2
-------------------------------------
Parent2 (P2) ^
-------------------------------------
P2 - Child 2
-------------------------------------
Parent3 (P3) >
-------------------------------------
Thanks in advance...

A couple of possibilities:
Use a TreeView, with suitable HierarchicalDataTemplates for the parent and child levels. I believe this will handle the arrow keys for you, but you may have to do more extensive templating to align everything correctly (ItemContainerStyle and the TreeViewItem.Template property would be the starting point).
Use an ItemsControl, and have your ItemTemplate include an Expander. The Header of the Expander would show the parent. The content of the Expander would be another ItemsControl, bound to the child items and with its ItemTemplate set to the appropriate detail view. Again, you would probably need to template the Expander to put the "expand/collapse" icon on the right rather than its default position on the left. You'll need to handle the arrow keys yourself in this case I think. The advantage is that this will naturally give you "stack" alignment (accordion style) rather than indenting.

Related

Switch WPF UI controls dynamically

I need to develop a simple WPF application. In the UI window, There are Labels and Text Blocks towards the left and Buttons towards the right.
Figure 1
Based on a config setting (whether the user is left-handed or right-handed) I need to switch the controls, Buttons towards the left and Labels and Text Blocks towards the right.
Figure 2
Can you please recommend a good way to address this requirement?
Depends what the scope of the app is likely to be.
2 alternatives:
1)
I think it likely as an app grows that there will be more than just buttons.
I would probably build a usercontrol which encapsulates this behaviour for a label and control. The usercontrol uses a static to decide where the textblocks are positioned but would look something like the row edit control in this:
https://gallery.technet.microsoft.com/WPF-Entity-Framework-MVVM-78cdc204
Which is a usercontrol has a contentpresenter in it so you can put any control you like ( such as a button ) "in" it and set a dependency property for the label.
2)
Define 2 contentcontrol templates similar to the one used in this:
https://social.technet.microsoft.com/wiki/contents/articles/28597.aspx
Put them in separate resource dictionaries and give them the same key.
Merge into application.current.resources the appropriate resource dictionary and hence style.
Seeing as this is an app setting, this is presumably a start up thing. People don't just change their "handedness" dynamically. So you could probably use these as staticresource. If they're realistically going to change at run time then I think this would be a bit more involved because you'd need to force re render of a view.
2 Templates are probably the right and stylish solution here as #RajN said.
Also you can define a grid with 2 columns and switch the property 'Grid.Column' of each controls accordingly
Maybe not the best way, but I managed to achieve this using a grid as per your suggestions. Thank you all for your valuable feedback.
I switched the columns and changed the widths accordingly.
if (AppSettings.IsLeft)
{
parentGrid.ColumnDefinitions[0].Width = new GridLength(400, GridUnitType.Pixel);
parentGrid.ColumnDefinitions[1].Width = new GridLength(1, GridUnitType.Star);
Grid.SetColumn(buttonGrid,0);
Grid.SetRow(buttonGrid,0);
Grid.SetColumn(contentGrid,1);
Grid.SetRow(contentGrid,0);
}

WPF Custom Control with Databinding

I'm looking to create a custom control which represents a hand.
This at needs to be bound to a datasource, then if a value/index value is present in the datasource which is representing a particular finger, the finger in question should appear green.
Can anyone point me in the right direction to start of with such control?
Basically I'm creating an app which records which fingers people where their rings and how many.
So graphics on each finger will show Green plus a number showing how many.
Rough Hand Design for User Control
Any help or direction will be mostly appreciative.
I'd recommend a usercontrol rather than custom control for this. As I think that link Clemens posted says, unless you really are going to switch out the template of the control then you don't need to do a custom control - which would be harder than a usercontrol.
This will have at least one dependency property you're going to bind your collection to. Make that an ObservableCollection. You can then pass say 0,1,0,2,0. If people change the rings they wear super dynamically you can set one of the collection to itself to cover change notification to the control.
Inside this I'd put a viewbox with a canvas in it.
Grab an outline of a hand from somewhere. You want to get a geometry out of this so look for a svg preferably.
Maybe https://www.flaticon.com/free-icon/stop-hand-silhouette_57659
Then download and install InkScape.
Use this to trace bitmap if that's all you have then save as > xaml.
Open that file in notepad and you'll see a path with a set of sort of co-ordinates. Grab those. These can be used to define a geometry you use as a resource or directly used as the Data to a path.
I use such a resource for the email "icon" in this:
https://social.technet.microsoft.com/wiki/contents/articles/32610.wpf-layout-lab.aspx
Or you could probably use one of the hand icons from syncfusion's metro studio ( free ) https://www.syncfusion.com/downloads/metrostudio
A Path can be used for your hand.
You then want 5 itemscontrols for your thumbs and fingers.
You could maybe make each of them a usercontrol as well but I'd try 5 controls for a first iteration.
They should template each item they're given into a green rectangle defined in itemtemplate.
Position 5 of them to taste on your hand's digits.
Bind their itemssources by index to your collection and use a converter to return the number of objects specified by that. So if it's 3 then you generate three objects.
That viewbox is there to scale everything. So you can size your control however you like and the rings will stay on the fingers.

I need advice on how to design/implement this custom panel

I've written a custom panel which displays its children docked either vertically or horizontally, separated by moving splitters in between. Since the Grid panel offers much of this functionality out-of-the-box, I just inherited from it.
To create the layout, upon Loaded is fired I do the following:
1) Read how many children it has and create the appropiate number of rows/colums.
2) Position every existing children in the corresponding row/colum.
3) Create, position and add a GridSplitter for every child.
This approach looks and works fine, but it opens the door to a lot of problems:
Since it's added a GridSplitter for each child, there are twice the number of expected children. If someone added 3 elements to it, Children.Count would return 6.
User could insert/remove things at the wrong place.
It just throws an exception when this Grid is used as the ItemsPanel for an ItemsControl, since in this case WPF (not Silverlight) does not allow direct children manipulation.
These 3 cases are the ones I've already tested, but I'm pretty sure a lot more would arise depending on what the user does with it.
As it turns out, this class must be regarded as 'implementation details', so the real question is, what control should I put in front of the user?
It sounds like it should be a Panel, but I can't control the Children property since it's not virtual, and there's also the ItemsControl which I think could be a good candidate, but I really don't know.
I'd much appreciate any kind of advice or some directions to do this the right way.
Thanks in advance.
You see using just grid you leave yourself with an imperative way of adding items only. As in
myCustomGrid1.AddMyItem(***), Grids simply don't have ItemsSource property. ItemsControls do - so if you need support for declarative items sources i.e. myControl.ItemsSource = {Binding ...} you're going to derive your control from ItemsControl. This is not a two liner - making your ItemsPanel Children writable is a big challange - there's no simple way of doing that.
This is all about a small thing overlooked during the Grid's design - splitters shouldn't have been added to Children collection, as Children are visulaizations of your BOs while spliiters are just formatting elements.
Here's what I would do.
Forget about ItemsSource & items altogether - it's aint worht the hassle. The only way to add/remove items to your control will be AddResiazableItem/RemoveResizbleItem. Calls will add items and splitter (for the middle items), extend the number of rows/cols of your grid depeneding on its orientation, set Grid.Row/Grid.Column attached properties for your visual children. You can keep your actual objects internally to support Orientation change.
If at any stage you'll want to bind your control to IEnumerable source - just create an attached behavior, which will iterate through the items and call AddResiazableItem within a loop.
Cheers.
P.S. To moderators - the editor seems to get broken, lads. I cant see the second item.
P.S.S. Got it fixed after a few tries.

Background color of rows in WPF datagrid

I've got a WPF datagrid bound to a list of parts and I need alternating row colors. The parts can also be part of a group, in which case the entire group needs to be the same color. Kind of like:
Part 1, group 1 - White background
Part 2, group 2 - Blue background
Part 3, group 3 - White background
Part 4, group 3 - White background
Part 5, group 4 - Blue background
Part 6, group 4 - Blue background
Part 7, group 5 - White background
The alternation of color must be based on groups, not simply by every other row. I have tried using grid.ItemContainerGenerator in the codebehind when the source collection is updated, but this does not work. ItemContainerGenerator.ContainerFromIndex() always returns null at this time, I suppose because the grid is still updating, I don't know. How can I do this?
In complex scenarios (like color denoting status, for example), I create a property on the viewmodel that returns a Color for the BackgroundBrush based on whatever rules you want. I typically create a static instance that I return repeatedly for each group, and freeze it, to conserve resources.
HTH...
I have used RowLoaded/RowUnloaded in the past, and using a behavior told the row what property of the rows datacontext contains the background. The problem that I have found is that binding a rows Background for some reason climbs up to the Grid's DataContext, instead of using the rows. If you are interested in this approach I can paste the code I have use. It does require that the ViewModel or ListViewModel be aware enough to set the correct colors for the rows. I have been often on the fence about colors in ViewModel, but color has become a business need and not just an ascetic effort.
What I did was overriding the following event of the DataGrid class
protected override void PrepareContainerForItemOverride(System.Windows.DependencyObject element, object item)
After calling the base method, you can set background color of the row based on the value of the item variable, which you can convert to group class and access properties such as group name for example.

Flattening out a TreeView in WPF

I have some hierarchical that I'd like to display in a TreeView, but formatted to look like a ListBox.
The data I have looks like this (with the possibility of any number of sub levels):
Item 1
Child 1
Child 2
Item 2
Child 3
Child 4
I'd like the data to be displayed like this (and wrap when need):
Item 1 Child 1 Child 2 Item 2 Child 3 Child 4
I'd like to use the TreeView so I can maintain the relationship between the parent and child items which is why I don't want to use a ListBox.
Thanks!
It sounds like the issue you have is that you don't have a clear separation between your data structure and your view. If you have a tree which is not WPF it should be easy to map a tree view onto it. If you have a tree, you can create an tree node numerator that can be used in a list view.
By doing this, the relationship never changes - just how the data is projected in the UI.
I think the best way would be to implement a recursive function that will scroll down your tree until it hit the end. That function would take a referenced list or collection as parameter and the current node.
here is the pseudo-code for it
Sub GenerateListFromTree(Node oNode, List oList)
AddItemToList(oNode.Name)
For each Node oChildNode in oNode.Nodes
GenerateListFromTree(oChildNode,oList)
Next
End Sub

Resources