Get GridViewColumn from control inside DataTemplate - wpf

Example:
<ListView Name="lvAnyList" ItemsSource="{Binding}">
<ListView.View>
<GridView>
<GridViewColumn Header="xx" DisplayMemberBinding="{Binding XX}" CellTemplate="{DynamicResource MyDataTemplate}"/>
<GridViewColumn Header="yy">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding YY}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
If I access the TextBlock inside DataTemplate I will have access to information about the bound path.
But I don't know how to find (starting from the TextBlock) its containing GridViewColumn (which is in the GridViewRowPresenter columns list) and compare it to the grid's GridViewColumn (from GridViewHeaderRowPresenter column list) to get the header's name.
At the end I should have the pairs: xx->XX, yy->YY
I can find a list of all TextBlocks inside ListView by using this:
GridViewRowPresenter gvrp = ControlAux.GetVisualDescendants<GridViewRowPresenter>(element).FirstOrDefault();
IEnumerable<TextBlock> entb = GetVisualDescendants<TextBlock>(gvrp);
I can find a list of all GridViewColumnHeaders:
GridViewHeaderRowPresenter gvhrp = ControlAux.GetVisualDescendants<GridViewHeaderRowPresenter>(element).FirstOrDefault();
And I cannot find the connection between the TextBlocks and the GridViewColumns...
I hope this is understandable.

GridViewColumns are abstract objects they never appear anywhere in the UI, you can try to name the column and bind it to the Tag of a control where you need it using ElementName or if that fails a Source with x:Reference on the name.

Related

How to bind to template-generated elements in a ListView

I have a ListView where for each item I'd like to add a TextBox for user input, and then be able bind to its content per selected item (or using an indexer) from outside of the ListView.
To illustrate:
<ListView Name="lv" ItemsSource="{Binding Source={StaticResource lvSrc1}}">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="Loc">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Name="tbGv" Text="User input" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
<TextBlock Text="{Binding ElementName=lv, Path=SelectedItem[tbGv]}" />
Of course the above binding won't work, as SelectedItem returns an object from the underlying collection, which has nothing to do with the added TextBox.
How can I access the generated TextBoxes using pure XAML?
Edit:
I've now found a couple of posts with somewhat similar problems, where solutions presented are trickier than I expected and not based on XAML.
So rather, would it be possible to access ListViewItem (not the data its bound to) and its properties? This way I think I could pass text from my TextBox through the Tag property of ListViewItem.
I'm quite new to WPF so I assume that my problem and its phrasing is ridiculous, but please be gentle ;)

Binding GrivdViewColumn's Width Property in XAML or in Code?

I would like to bind GrivdViewColumn's Width Property with my ViewModel. Lets say an instance of ViewModel is the Datacontext of the Window. ViewModel has got Properties called IdWidth, LastNameWidth, FirstNameWidth. And that Window contains the following given ListVeiw in a stackPanel. Everything is fine but somehow the Width Property of GridViewColumn is not bound. Please post the code after making sure that it works.
<ListView Name="PatientListView"
ItemsSource="{Binding Patients}"
SelectionMode="Single">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="Patient-Id"
Width="{Binding IdWidth, Mode=TwoWay}"
DisplayMemberBinding="{Binding PatientId}"/>
<GridViewColumn Header="Last Name"
Width="{Binding LastNameWidth, Mode=TwoWay}"
DisplayMemberBinding="{Binding LastName}"/>
<GridViewColumn Header="Last Name"
Width="{Binding FirstNameWidth, Mode=TwoWay}"
DisplayMemberBinding="{Binding FirstName}" />
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
The accepted answer is wrong as outlined in my comment, looking at your paths you seem to expect the DataContext to be the item, which cannot work (there are multiple items to begin with, which should be chosen?).
The DataContext of the columns is the DataContext of the ListView.
This example will result in a column with the Header Lorem Ipsum:
<ListView>
<ListView.DataContext>
<Label Content="Lorem Ipsum"/>
</ListView.DataContext>
<ListView.View>
<GridView>
<GridViewColumn Header="{Binding Content}"/>
</GridView>
</ListView.View>
</ListView>
Also you might be interested in this article on debugging databindings.
Edit: FrameworkElement is not required for DataBinding, sorry for this incorrect interpretation of the citation from MSDN below.
See comments and #H.B. 's answer
GridViewColumn does not inherit from FrameworkElement, which is required for databinding.
see MSDN:
Support for data binding and dynamic resource references: [...], but the ability to resolve a member value that is stored as an Expression (the programming construct that underlies both data binding and dynamic resources) is implemented by FrameworkElement

How to get selected item of a nested listview?

i have a nested listview, i can bind the selected item of the basic listview to my viewmodel but not my selected item of the nested listview ( in the basic listview ) I just do:
this is my listview:
<ListView Height="155" ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Visible" dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="False" Margin="24,506,280,169" Background="#CDC5CBC5"
dd:DragDrop.DropHandler="{Binding}" SelectedItem ="{Binding Path=SelectedCluster,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" ItemsSource="{Binding Path=Clusters,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" >
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="Titel" DisplayMemberBinding="{Binding Title}"/>
<GridViewColumn Header="Questions">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ListView DataContext="{Binding}" ItemsSource="{Binding ExaminationQuestions}" SelectedItem="{Binding Path=SelectedExaminationQuestionInCluster,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}">
<ListView.View>
<GridView>
<GridViewColumn Header="Description" DisplayMemberBinding="{Binding Question.Description}"/>
</GridView>
</ListView.View>
</ListView>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
Viewmodel:
public ExaminationQuestion SelectedExaminationQuestionInCluster
{
get { return selectedExaminationQuestionInCluster; }
set { selectedExaminationQuestionInCluster = value;
OnPropertyChanged("SelectedExaminationQuestionInCluster");
}
}
Someone who knows what i am doing wrong? If i set a breakpoint of the setter of selecteditem of the second listview. He just ignores that..
Thanks
My guess is the binding is probably incorrect. In your outer ListView, you bind to "Clusters". Your inner ListView is probably trying to bind to "SelectedExaminationQuestionInCluster" on the current Cluster. You can see if this is the case by using snoop. It's a valuable tool when debugging WPF apps. It will highlight broken bindings in red and tell you what's wrong with them.
If you want to bind to "SelectedExaminationQuestionInCluster" on the parent DataContext, you could use this syntax:
SelectedItem="{Binding Path=DataContext.SelectedExaminationQuestionInCluster,
ElementName=OuterListView}"
You'll have to give the outer ListView a name of course.
EDIT: I just realized this might not make sense. If each Cluster has its own collection of ExaminationQuestions, then each Cluster should also have a SelectedExaminationQuestion. The parent DataContext should not have any concept of a SelectedQuestion unless it is shared amongst all Clusters.

How to bind to ColumnCollection for a GridView using XAML

I have a collection of objects which I would like to represent columns in a GridView.
Currently I am binding individual columns in my GridView to items in the collection like this:
<ListView>
<ListView.View>
<GridView>
<GridViewColumn Header="{Binding Path=MyColumns[0].Title}"></GridViewColumn>
<GridViewColumn Header="{Binding Path=MyColumns[1].Title}"></GridViewColumn>
<GridViewColumn Header="{Binding Path=MyColumns[2].Title}"></GridViewColumn>
</GridView>
</ListView.View>
</ListView>
This works well, but what I really want to do is to bind to the collection itself (since the number of items in the collection might change at run-time). I would like to accomplish this in XAML. What I have in mind is something like this (note this does not work - the ColumnsSource attribute is make-believe):
<ListView>
<ListView.View>
<GridView ColumnsSource="{Binding Path=MyColumns}">
</GridView>
</ListView.View>
</ListView>
Can this be done? If so, how?
You can use an attached property to create a ColumnsSource property, see this question for an example.

populate listview dynamically

If i bind a data table to WPF Toolkit-data-grid.....no need to specify the column names there...it will automatically take that from the datable.
But if I am binding a list-view to an observable collection...I have to specify the column header names for each column one by one..in xaml file.
So if I have a list of column names ->List<ColumnHeaderNames>
along with
list of item to populate ->List<Object to populate list>
I can bind item list to list-view...and column name list to list-view header...but i don't know if there s any property in list-view...to bind my header.
That means...
I have a user control in which i have 2 list-views ...one is available and another is selected. I need this user control shud be reusable...That means...if I am binding a list like ...the list shud contain two columns...first column with name as "state" and second column name as "county". But if I am binding a list like ..Then listview shud contain 3 columns...with column names as fruit, color and price.
I think the best for you would be to set the View property of your ListView to a GridView. Then you can easily bind the columns header :
<ListView ItemsSource="{Binding ListOfValues}">
<ListView.View>
<GridView >
<GridViewColumn DisplayMemberBinding="{Binding XVal}" Header={binding header}"/>
</GridView>
</ListView.View>
</ListView>
for more information you can go there http://msdn.microsoft.com/en-us/library/system.windows.controls.gridview.aspx
And if you use a struct which contains the list and the name : < {listOfFruits,"Fruits"}, {listofStates,"States>
And then :
<ItemsControl ItemsSource="{Binding ListOfStruct}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ListView ItemsSource="{Binding ListOfitem}">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding}" Header="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}, Path=DataContext.Header}"/>
</GridView>
</ListView.View>
</ListView>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
It is not the best way but I don't know how to do

Resources