WPF: finding the location of an element - c#

Before answering, it is not as easy question as you might have thought about when you read the title.
I have an ItemsControl which is binded to an ObservableCollection of T and data being described as a DataTemplate. So far it is a classic case.
When I add a new element I need to know the exact coordinate and positions inside the window of the element being rendered.
I realize there is a passage of time needed for the Collection to raise the event, and WPF to use all his layouting mechanism to actually position the element.
I wish to be notified when it is done and grab those locations.
I am using those lines of code
UIElement item = list.ItemContainerGenerator.ContainerFromItem(foo) as UIElement;
Point point = TranslatePoint(new Point(0.0, 0.0), Window.GetWindow(item));
The problem is now when I hit those lines it is always premature, If I "wait" for a second and let wPF finish, I do get the right location.
I am trying to find better solutions than "waiting" for the UI-Thread.
Maybe you can help out :)
Thanks!
Ariel

Probably one of UIElement's events will tell you when the layout data is available. LayoutUpdate looks promising.

Related

How do I make controls extend passed a control bound or overlap with others?

I have a wpf grid setup where I have two custom buttons that are next to each other. First picture is how the design window looks like, however, functionally, it looks like the second picture. I want them to function like the grid doesn't block them (closely resembling the first picture.)
The current xml I have is based on this MSDM which is very basic.
I do not know how to proceed. Do I have to use a different control panel/container or is there a setting to allowed them to extend passed the grid if the other button isn't above it (like zpanel?)
E: I couldn't find any other questions for this, so please link to any searches/posts with information on it.
Turns out I can use canvas and just do a bit more xml to keep the design the same. I would still like to know if it is possible to overlap them in any way for future use.

Best way to handle sequence of clicks in wpf

Let assume I have a canvas filled with rectangles, databound to a viewmodel.
Now I want to "select" a rectangle, so I've added a Select command to the rectangle class, and bound that to the rectangles.
This works fine.
The problem is this selection will have consequences. Easiest example is that selecting one rectangle should deselect any previous.
One way to deal with this would be to change the command (using relaycommands here) from
_ => this.Selected = true
to
_ => ParentVM.SetSelected(this)
This feels a bit nasty. "Loosening" the 2-way dependency by some DI framework just feels contrived in this example.
I'm guessing another way would be to solve this at the "WPF level", by using the bubbling of events, but I'm not sure that it's a better solution?
Anyone have any good suggestion?
EDIT: Got a good tip in the comments below about inheriting "Selector". This is an excellent suggestion, but unfortunately I left out a detail above, that i didn't think mattered.
The real problem is that I want to first select one of the rectangles, and then another one and get, say, the distance between them displayed. So one of them is the "first selected" and the other is the "second selected".
Unless I am not understanding the requirement, this is what I would do.
Create a property (on the VM if using MVVM) which will hold a reference to the last rectangle which has been selected.
Require each rectangle to report itself when its selected, by either, owning a bound reference to that aforementioned property or by using a commanding operation where it reports itself as the reference.
Inside the setter of the property created, take the old one (if it exists) and the new one, determine the distance and put that on another property on the VM and then hold the reference to the currently selected one.

Updating treeview control in WPF

In my application as soon as an object is dropped on canvas I stored it in a tree. Now, I'm trying to write a method to delete an object which is fine but I also need to delete that specific object from the tree. Each object has a unique id so this way I can find it in tree and remove it from tree while being deleted from the canvas.
In order to find a node in tree I have planned to store ID in each node (item.Tag), however, I'm facing two problems:
How can I access to details of a node from a different class? myTreeObj.Items.Tag doesn't work properly.
When I loop in tree myTreeObj.Items.Count shows more items that what I see.
Any comments will be appreciated.
Thanks.
In WPF, TreeViews are actually what they say they are: a view of a data structure. In WinForms, you had to crawl up and down the nodes of a TreeView and add them or remove them manually. In WPF, the proper approach is to add or remove items from the actual data hierarchy (to which the TreeView is binding) and use PropertyChanged or CollectionChanged notifications to tell the TreeView to update itself automatically.
What you are trying to do may be possible, but it is going to be an uphill fight all the way, and things will not work as expected. WPF REALLY wants you to use databinding, and any other approach is going to give you headaches.
This article may be a helpful place to start understanding how to work with the WPF TreeView:
http://joshsmithonwpf.wordpress.com/2008/05/24/the-wpf-treeview-is-a-view-of-a-tree/
EDIT:
The author's original article is actually more helpful:
http://www.codeproject.com/Articles/26288/Simplifying-the-WPF-TreeView-by-Using-the-ViewMode

Getting the SelectedItem as DataContext for a completly independent Uielement

I began to make a program in c# using wpf and the mvvm pattern to learn this program language. I feel sorry that I finally have problems I can't solve using Google. But I try being precise.
At first I present you the GUI, so I can explain my problems more easily.
The xaml file of the MainWindow comes here.
As you see, it's not a serious and important program, I just made it to learn some techniques.
Now comes my main question:
I need the SelectedItem Property of the second ListView(Consumables) in the footer. I thought this wouldn't be a serious problem cause I can just bind it to the selected item using relative path and the name of the listview. This didn't work and caused a null reference error as soon as I added an item to this listview. I double checked the code and I am sure I haven't made something wrong there. So I thought the problem is, that it's kind of a descendant ui element.
My next try was to create a new property in my main view model which is bound to the selected item. You can find this in line 136. But as I found out, this causes me a System.NullReferenceException too if i click on an item. I think it's because this property is read only. I don't know what to do. Isn't there any other possibility to bind the SelectedItem as DataContext for the footer?
My Second Question is about line 27. The TabControl should always select the first Tab automatically. But it makes it only every second time. It's quite funny. If I scroll down the Champion Combo Box, the first tab goes: selected - unselected - selected - ...
My third and last Question is about 72 which is similar to line 50 (The DataContext is the same too) But the ICommand of the ContextMenu of the itemtemplate you see line 50 works, whereas the contextmenu (l. 72) does not call the Icommand. Weird.
It's a fun project, so I can provide you the source code if my information are not enough.
--Sorry, only two hyperlinks allowed--
Please help me. I am just a bit confused by this unsuspected behaviour. I haven't found anything that could help me and I'm sorry if the answers of these questions will be simple :P
Just to emphasize it: The main question is the only one I really need an answer for. The second one could be solved programatically. And to solve the third one, I could just remove this feature.
I'm happy for EVERY HINT!
Haven't gone through the entire question but you should bind selected item to your view model property (two way binding) and then bind another UIElement to this view model property (probably one way binding or check for value equality as to not cause stackoverflow exception)

How do I access the controls in a WPF DataGrid

In good old (well!!) WinForms days the datagrids row used to the be the actual control and you could then access the DataItem.
In WPF its all flipped and dataGrid.Items is just the source data.
I am probably doing this the wrong way round as im a bit of a WPF newb but how can I iterate through the rows of my gridview grabbing the values from certain labels, textboxes etc?
Yes, you are doing this the wrong way round. What you should be doing is iterating through the items in your data source - it's where all the values are, after all.
It's possible to iterate through the WPF objects, but it's not trivial. And there's a significant problem that you'll run into if you try.
You can use the VisualTreeHelper class to search the visual tree and find the DataGrid's descendant objects. If you play with this long enough, eventually you'll figure out how to find the specific controls you're looking for. But the DataGrid (actually, the VirtualizingStackPanel in its control template) virtualizes its visual children. If an item hasn't appeared on the screen yet, its WPF objects haven't been created yet, and you won't find them in the visual tree. You may not be able to find what you're looking for, not because you don't have a way of finding it, but because it doesn't exist.
If you're using value converters and formatting in your bindings (which is the only reason I can think of that you'd want to look at the WPF objects and not the underlying data items), I'm afraid the answer is: don't do that. Do the value conversion and formatting in your data source, and expose the results as properties that can be bound to directly.
It's certainly possible to use WPF without using the MVVM pattern. But this is the kind of brick wall that you can run into if you don't.
You can use this
public DataGridRow TryFindRow(object item, DataGrid grid)
{
// Does not de-virtualize cells
DataGridRow row = (DataGridRow)(grid as ItemsControl).ItemContainerGenerator.ContainerFromItem(item);
return row;
}
where item represent the data displayed on the row.
Hope this helps.

Categories

Resources