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

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

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 ;)

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.

Sorting a ListView by deriving GridViewColumnHeader

I am trying to implement a ListView with a GridView with sortable columns.
To sort the ListView I hook up the Click event for the GridViewColumnHeaders and adding SortDescriptors to the default view source (similar to what is done in MSDN).
Something like this:
<ListView ItemsSource="MY ITEMS SOURCE BINDING">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn DisplayMemberBinding="MY DISPLAYMEMBER BINDING">
<GridViewColumnHeader Content="My Header" Click="ColumnHeaderClicked"/>
This all works fine, but I would like to generalize it a bit. To do that I simply derived GridViewColumnHeader and wrote a click-handler. I know there are many sortable list view implementations out there typically deriving from ListView, but I was just wondering if this approach is possible.
Something like this:
<ListView ItemsSource="MY ITEMS SOURCE BINDING">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn DisplayMemberBinding="MY DISPLAYMEMBER BINDING">
<local:SortableGridViewColumnHeader Content="My Header"/>
For this to work I need to navigate from the SortableGridViewColumnHeader code to the containing ListView in order to set new SortDescriptors.
I tried navigating up the Parent ladder, but the GridViewColumnHeader is not a visual child of my ListView. Surely I could make a dependency property and bind it to the ListView, but there must be a way to navigate to it instead.
How would I do that in code? (I am not looking for answers on how to sort a WPF ListViews in general, I am wondering if it can be done this way).
EDIT
It turned out that what I needed was this parent searcher in the click-handler of my GridViewColumnHeader derivative.
DependencyObject parent = this;
do
{
parent = VisualTreeHelper.GetParent(parent);
if (parent == null) return;
} while (!(parent is ListView));
Now my sorting works like a charm.
There is a much easier way to do that, using an attached property. Check out this article for details.
<ListView ItemsSource="{Binding Persons}"
IsSynchronizedWithCurrentItem="True"
util:GridViewSort.AutoSort="True">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="Name"
DisplayMemberBinding="{Binding Name}"
util:GridViewSort.PropertyName="Name"/>
<GridViewColumn Header="First name"
DisplayMemberBinding="{Binding FirstName}"
util:GridViewSort.PropertyName="FirstName"/>
<GridViewColumn Header="Date of birth"
DisplayMemberBinding="{Binding DateOfBirth}"
util:GridViewSort.PropertyName="DateOfBirth"/>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>

How to set property only on second column of a ListView?

Introduction
I have a ListView and want to format only the second column. The following XAML code does that:
<ListView x:Name="listview">
<ListView.View>
<GridView>
<GridViewColumn Header="Property" DisplayMemberBinding="{Binding Path=Key}" Width="100"/>
<!-- <GridViewColumn Header="Value" DisplayMemberBinding="{Binding Path=Value}" Width="250">-->
<GridViewColumn Header="Value" Width="250">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Value}" Foreground="CornflowerBlue" AutomationProperties.Name="{Binding Path=Key}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
The one problem I have is that the AutomationProperties.Name property is not being set. I was checking it with the Coded UI Test Builder and the property is empty. The Text and the Foreground property are being set correctly.
Question
Does anyone know why AutomationProperties.Name is not being set?
Additional information
Strangly enough, the following XAML code does set the AutomationProperties.Name
<ListView x:Name="listview">
<ListView.Resources>
<Style TargetType="TextBlock">
<Setter Property="AutomationProperties.Name" Value="{Binding Key}"/>
</Style>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn Header="Property" DisplayMemberBinding="{Binding Path=Key}" Width="100"/>
<GridViewColumn Header="Value" DisplayMemberBinding="{Binding Path=Value}" Width="250"/>
</GridView>
</ListView.View>
</ListView>
The problem here though is that AutomationProperties.Name is being set on all the columns. But I only want it on the second one because otherwise my Coded UI Test code returns the wrong value (that of the first column, instead of that of the second column which I want).
Don't know if you're aware of this, but a very helpful tool in analyzing these types of problems is Snoop.
In particular, it will highlight (with red) any data binding errors you may have.
I took a look myself and it sure seems as if the first piece of xaml (above) is now working (after you cleared up the syntax error). In Snoop, bound properties are highlighted with a light green.
Here is a screen shot of Snoop showing the property is being set correctly:
And here is a screen shot of Snoop showing the TextBlock (where the property isn't set ... no light green column) on the first column:
And, finally, I've intentionally broken the binding to show you what Snoop shows when something is wrong and you have a data binding error (it is highlighted in red and one of the columns gives you additional information):

Multicolumn listview in WPF at design time

I might sound dumb here, but I want to do something really simple. At design time, I want to add columns to a listview control and add some data to it. I need to add combobox in each column of the listview. The thing I am not able to find is where to mention the column number in the listviewitem. Any help appreciated guys.
<ListView IsSynchronizedWithCurrentItem="True" Margin="8,68,304,188"
BorderThickness="2,2,2,2">
<ListView.View>
<GridView>
<GridViewColumn Width="150" Header="Column1"/>
<GridViewColumn Width="150" Header="Column2"/>
</GridView>
</ListView.View>
<ListViewItem>
</ListViewItem>
</ListView>
Each column in the listviewitem is rendered based on the GridView definition, so there is no real concept of column numbers. What you do is bind objects to the the listview's itemsource and it creates listviewitems from it. Thus, there are a few hoops to jump through.
This link has an example of how to do some simple object data binding. The advantage of this is what binding structure you have for design time can probably be reused for run-time if you set the datacontext/itemsource to an empty object instead of the static one in XAML.
If you're doing this to show examples or you just have a static data source that you want to use, I would recommend using the XmlDataProvider. Then you'd change your ListView to be like this,
<ListView IsSynchronizedWithCurrentItem="True" Margin="8,68,304,188"
BorderThickness="2,2,2,2">
<ListView.View>
<GridView>
<GridViewColumn Width="150" Header="Column1" DisplayMemberPath="{Binding XPath=/A/B}"/>
<GridViewColumn Width="150" Header="Column2" DisplayMemberPath="{Binding XPath=/A/C"/>
</GridView>
</ListView.View>
<ListViewItem>
</ListViewItem>
</ListView>

Resources