I'm trying to bind to properties on my GridView's DataContext and use a DataTemplateSelector to assign the correct template to the cell, but I can't seem to find the correct way to do this. Binding to DisplayMemberBinding overrides the template selector, but setting the CellTemplateSelector property binds to the DataContext rather than the properties I want to select templates for.
This answer seems to describe exactly what I'm looking for, but I'm having trouble finding information on how to implement what it describes: https://stackoverflow.com/a/12519433/1756960 .
This is what I tried using that isn't working (simplified for posting):
<ListView ItemsSource="{Binding Items}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name"
DisplayMemberBinding="{Binding Name}"
CellTemplateSelector="{StaticResource ContentTypeTemplateSelector}" />
<GridViewColumn Header="Data"
DisplayMemberBinding="{Binding}"
CellTemplateSelector="{StaticResource ContentTypeTemplateSelector}" />
</GridView>
</ListView.View>
</ListView>
The first thing I would recommend doing is to differentiate your Content Template selectors in one of two ways. The First is to simply have more than one template selector class. the second is to have two instances, whose templates are assign different bindings.
<Resources>
<ns:TemplateSelector x:Key="NameTemplateSelector">
<ns:TemplateSelector.Template1>
<DataTemplate>
<!-- Something bound to Name -->
</DataTemplate>
</ns:TemplateSelector.Template1>
</ns:TemplateSelector>
<ns:TemplateSelector x:Key="DataTemplateSelector">
<ns:TemplateSelector.Template1>
<DataTemplate>
<!-- Something bound to Data -->
</DataTemplate>
</ns:TemplateSelector.Template1>
</ns:TemplateSelector>
The reference to Attached Properties (see MSDN) would have you make a property, attach it to the template selector, and then access that data from the TemplateSelector's code.
Related
I have an WPF application with this XAML...
<ListView
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
ScrollViewer.VerticalScrollBarVisibility="Hidden"
ItemsSource="{Binding HTMLControlNames}">
<ListView.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}"></Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The bindings are correct and there is data in the ObservableCollection property that implements INPC.
I added the Expression Dark Theme which is also working but this is the output from the markup above:
The buttons are there, they just aren't showing the text... BTW the number of buttons is equal to the count of items in the collection.
Here's the property in the ViewModel, single stepping shows me there are items (the proper ones) in the collection.
public ObservableCollection<ControlName> HTMLControlNames
{
get { return _HTMLControlNames; }
set
{
_HTMLControlNames = value;
PropChanged("HTMLControlNames");
}
}
Lastly the ControlName Class:
public class ControlName
{
public string Name { get; set; }
}
If I don't use ExpressionDark.xaml the content shows up!
Here's more information: Top part of the control shows up ok...
If I don't use DataTemplate as in the top half of this control the buttons are fine.
Here's the default Control Template (just a grid with a content presenter)..
What you are seeing that you think is a button in the ListView control is actually the header area. You can see this more clearly by adding columns to the header:
<ListView.View>
<GridView>
<GridViewColumn Header="Test" />
</GridView>
</ListView.View>
The list view items are actually the thin lighter-grey strips below this header.
I've not had much experience in templating list views (I usually use list boxes), but in the theme you are using, the list view seems to be templated in such a way that it basically ignores the item template. You can see that it does by using a daft template like this:
<ListView.ItemTemplate>
<DataTemplate>
<Rectangle Fill="Orange" Width="20" Height="20" />
</DataTemplate>
</ListView.ItemTemplate>
You will see that no orange rectangles appear in the list view, even when the collection that you bind to has elements in it.
I'm not quite sure how you can get around this problem. Maybe someone with more experience of templating list view controls can chime in.
Thanks to Stephen this is the solution so far:
<ListView.View>
<GridView>
<GridViewColumn x:Name="xHtmlControlcol"
Header="Filter For:"
Width="{Binding ActualWidth,
ElementName=XListView,
Mode=OneWay}">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Content="{Binding Name}"
Width="{Binding ActualWidth,
ElementName =XListView,
Mode=OneWay}" BorderThickness="1,0" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
It produces this: Note that there's still one small issue with the left margin which I don't know how to fix right now.
The Trick to this is that when using Data Binding:
Use the ItemsSource to bind to collection of ListView
Use the ListView.View as shown above to set up a GridViewColumn.
Use the GridViewColumn.CellTemplate to inject the control type you want.
Make sure the injected control is bound to the proper Content path name.
<ListView Grid.Row="1" Margin="10" Name="lvRegistersConfig" ItemsSource="{Binding registers}">
<ListView.Resources>
<local:BoolToVisibility x:Key="BTVConverter"/>
</ListView.Resources>
<ListView.View>
<GridView>
<local:GridViewColumnExt Header="Register Name" Width="100" Visibility="{Binding Vis, Converter={StaticResource BTVConverter}}" >
<local:GridViewColumnExt.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" Visibility="{Binding Vis, Converter={StaticResource BTVConverter}}"/>
</DataTemplate>
</local:GridViewColumnExt.CellTemplate>
</local:GridViewColumnExt>
</GridView>
</ListView.View>
</ListView>
GridViewColumnExt is a class that inherits from GridViewColumn and adds the Visibility property.
My ListView's ItemsSource as you may see, is set to be the registers ObservableCollection. Register class has a property of type bool named Vis.
It all works fine for the TextBox, but from the GridViewColumnExt don't, i think i cannot reach the collection and bind to the specific object.
I am not sure what's the DataContext for the GridViewColumn, i saw that i cannot set it.
I need a hint on this, how can my GridViewColumn see the Vis property from the Register object in the registers ObservableCollection ?
I'm not sure how the GridViewColumnExt control works, but it looks like with your XAML that the DataContext is not going to be individual item in the registers collection, rather it is the same DataContext on which the registers collection is declared. So, you're probably not going to be able to control the visibility of the column with the Register.Vis property.
If you're looking to put more in your cell template, you could wrap the TextBlock in a Grid and bind the Grid.Visibility property to the Register.Vis property.
Does that make sense?
What I want is to have a table having a first column with every row named by me one by one, not binding to some property. But all of the second column should be binded to a property. I cannot do these two with gridview since it only makes binding and does not let me to write something manually. On the other hand, I havent seen any flowdocument table tutorial in which someone mention binding. So what is the proper object suitable to fulfill mentioned two things?
ListView and GridView should meet your requirements. You have the ability to set bindings on the GridViewColumns by using DisplayMemberPath or in the CellTemplate. You can also choose not to use bindings in a column.
Take this for example:
<ListView ItemsSource="{Binding NameValuePairs}">
<ListView.Resources>
<DataTemplate x:Key="FirstCellTemplate">
<Grid Width="100">
<TextBox Width="75"/>
</Grid>
</DataTemplate>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn CellTemplate="{StaticResource FirstCellTemplate}"/>
<GridViewColumn DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn DisplayMemberBinding="{Binding Value}"/>
</GridView>
</ListView.View>
</ListView>
NameValuePairs is an ObservableCollection<NameValuePair> and NameValuePair is a view model with a Name and Value properties. The first column is contains a TextBox on each row, the second is a TextBlock with the Name, and the third is a TextBlock with the Value.
I have a GridView that displays some values:
<ListView ItemsSource="{Binding MyDataSource}">
<ListView.View>
<GridView>
<GridViewColumn Header="Date1" DisplayMemberBinding="{Binding Date1}" />
<GridViewColumn Header="Date2" DisplayMemberBinding="{Binding Date2}" />
...other Columns, not necessarily containing dates...
</GridView>
</ListView.View>
</ListView>
This works fine. Now I want to create a data template that formats a date in a specific way:
<DataTemplate x:Key="MySpecialDate">
<TextBlock Text="{Binding StringFormat={}{0:yyyy.MM.dd}}" />
</DataTemplate>
Adding CellTemplate won't work as long as DisplayMemberBinding is specified. Thus, I have to remove the DisplayMemberBinding attribute:
<GridViewColumn Header="Date1" CellTemplate="{StaticResource MySpecialDate}" />
<GridViewColumn Header="Date2" CellTemplate="{StaticResource MySpecialDate}" />
Here's the question: Now that DisplayMemberBinding is gone, how do I tell the GridView which property to display? GridViewColumn does not have a DataContext property.
Of course, I could put the name of the property (Date1 resp. Date2) into the DataTemplate, but then I would need one template for each property and that would defeat the whole purpose of having a template.
<!-- I don't want that -->
<DataTemplate x:Key="MySpecialDate1">
<TextBlock Text="{Binding Date1, StringFormat={}{0:yyyy.MM.dd}}" />
</DataTemplate>
<DataTemplate x:Key="MySpecialDate2">
<TextBlock Text="{Binding Date2, StringFormat={}{0:yyyy.MM.dd}}" />
</DataTemplate>
Related to your question is mine: Pass multiple resources into a DataTemplate which I finally found a solution for.
I think you cannot get around the definition of a template for every such date. But depending on the complexity of your display template this solution keeps the additional DataTemplates code to a minimum:
<DataTemplate x:Key="DateX">
<ContentPresenter ContentTemplate="{StaticResource MySpecialDate}" local:YourClass.AttachedDate="DateX"/>
</DataTemplate>
These additional DataTemplates use an attached property which can then be used by a converter in the DataTemplate "MySpecialDate".
The attached property has to be defined in the code behind. The answer to my own question contains a complete example.
If the format is really that widely used, then you could use the following
In DateTimeColumn.cs
public class DateTimeColumn : GridViewColumn
{
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (Equals(e.PropertyName, "DisplayMemberBinding") && DisplayMemberBinding != null)
{
DisplayMemberBinding.StringFormat = "{0:yyyy.MM.dd}";
}
}
}
XAML code...
<local:DateTimeColumn DisplayMemberBinding="{Binding Date1}" />
(I don't like the format as a magic string, but the code can be modified as needed.)
I'm trying to create a Table inside a FlowDocument inside a FlowDocumentPageViewer, as seen in this tutorial. I've never created a table in WPF before, and I was expecting there to be an ItemsSourceproperty to which I could bind, like with a ListBox. I want to customize the template for each row, and I want each row's source to be an item in a collection that I pass to the entire table. So I'll have a List<MyClass> instance that would be passed to the table, and each row would show a single MyClass. How can I do that? Do I not want to use a Table within a FlowDocument? The reason I wanted to use some kind of tabular format is so that I can have a single row of column names that applies to all the rows.
I think what I wanted was to use ListView, thanks to this tutorial:
<ListView Grid.Row="0" ItemsSource="{Binding Path=MyList}" MinWidth="400"
MinHeight="200">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn
DisplayMemberBinding="{Binding Path=MyFirstField}"
Header="First Field"/>
<GridViewColumn
DisplayMemberBinding="{Binding Path=MySecondField}"
Header="Second Field"/>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>