Rather than declaring a tab for each of the ObservableCollections explicitly as in the first TabControl below I need them to generate dynamically as in the second TabControl and have the ItemsSource of the nested ListView set to each of the nested ObservableCollections.
In other words: Why aren't the ItemSource bindings of the nested ListViews in the second TabControl working? Is there a way to set the index of the nested ObservableCollection to the index of the containing ObservableCollection?
Or: How do I make the second dynamic TabControl and nested ListViews look like the first static TabControl and nested ListViews?
using System.Collections.ObjectModel;
using System.Windows;
namespace GridViewColumns2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ViewModel viewModel = new ViewModel();
viewModel.ThingCollections = new ObservableCollection<ThingCollection>();
viewModel.ThingCollections.Add(new ThingCollection { Name = "One" });
viewModel.ThingCollections[0].Things = new ObservableCollection<Thing>();
viewModel.ThingCollections[0].Things.Add(new Thing { Name = "One.One" });
viewModel.ThingCollections[0].Things.Add(new Thing { Name = "One.Two" });
viewModel.ThingCollections[0].Things.Add(new Thing { Name = "One.Three" });
viewModel.ThingCollections.Add(new ThingCollection { Name = "Two" });
viewModel.ThingCollections[1].Things = new ObservableCollection<Thing>();
viewModel.ThingCollections[1].Things.Add(new Thing { Name = "Two.One " });
viewModel.ThingCollections[1].Things.Add(new Thing { Name = "Two.Two" });
viewModel.ThingCollections[1].Things.Add(new Thing { Name = "Two.Three" });
DataContext = viewModel;
}
}
public class ViewModel
{
public ObservableCollection<ThingCollection> ThingCollections { get; set; }
}
public class ThingCollection
{
public string Name { get; set; }
public ObservableCollection<Thing> Things { get; set; }
}
public class Thing
{
public string Name { get; set; }
}
}
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TabControl>
<TabItem>
<TabItem.Header>One</TabItem.Header>
<TabItem.Content>
<ListView ItemsSource="{Binding Path=ThingCollections[0].Things}">
<ListView.View>
<GridView>
<GridViewColumn Width="120" Header="Name" DisplayMemberBinding="{Binding Name}" />
</GridView>
</ListView.View>
</ListView>
</TabItem.Content>
</TabItem>
<TabItem>
<TabItem.Header>Two</TabItem.Header>
<TabItem.Content>
<ListView ItemsSource="{Binding Path=ThingCollections[1].Things}">
<ListView.View>
<GridView>
<GridViewColumn Width="120" Header="Name" DisplayMemberBinding="{Binding Name}" />
</GridView>
</ListView.View>
</ListView>
</TabItem.Content>
</TabItem>
</TabControl>
<TabControl Grid.Column="1" ItemsSource="{Binding Path=ThingCollections}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ListView ItemsSource="{Binding Path=ThingCollections[0].Things}">
<ListView.View>
<GridView>
<GridViewColumn Width="120" Header="Name" DisplayMemberBinding="{Binding Name}" />
</GridView>
</ListView.View>
</ListView>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
When you're in a DataTemplate, the DataContext is already that of the item (in your case, a ThingCollection class.
So instead of:
<ListView ItemsSource="{Binding Path=ThingCollections[0].Things}">
Use:
<ListView ItemsSource="{Binding Path=Things}">
To diagnose future binding errors, hit F5 (start debugging) and then take a look in the Output Window. All binding errors will be recorded there. In this case, it would've indicated that it cannot find a property named ThingCollections in the object ThingCollection.
Remove ThingCollections[0]. in BindingPath. The DataContext will be set for you to the right ThingCollection.
<ListView ItemsSource="{Binding Path=Things}">
Related
this is XAMl code
Image Icon and string don't appear in ListView in WPF
What's wrong?
Not only the image, but also other text strings do not appear. Trying other methods says that the ItemSource is overlap.
Is the binding not being applied?
<GroupBox Grid.Column="1" Grid.RowSpan="2" Background="Aqua">
<StackPanel Orientation="Vertical" Background="Aquamarine" Margin="5">
<StackPanel.Resources>
<Style x:Key="ListviewStyle" TargetType="{x:Type ListView}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListView}">
<Border x:Name="corner"
CornerRadius="9"
Background="White"
Height="1000">
<ContentPresenter
VerticalAlignment="Center"
HorizontalAlignment="Center"
/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Resources>
<TextBlock Text="I/O Input"
FontSize="30"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Margin="10"/>
<ListView Style="{StaticResource ListviewStyle}"
x:Name="lv_input">
<ListView.View>
<GridView>
<GridViewColumn Width="50" Header="InputStatus">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding InputStatus}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="200" Header="InputName"
DisplayMemberBinding="{Binding InputName}"/>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</GroupBox>
this is behind code
Image Icon and string don't appear in ListView in WPF
What's wrong?
Not only the image, but also other text strings do not appear. Trying other methods says that the ItemSource is overlap.
Is the binding not being applied?
enter code here
public partial class IOSetPage : Page
{
public ObservableCollection<ListViewItemsData> ListViewItemsCollections { get { return ListViewItemsCollections; } }
ObservableCollection<ListViewItemsData> _ListViewItemsCollections = new ObservableCollection<ListViewItemsData>();
public IOSetPage()
{
InitializeComponent();
ListViewItemsCollections.Add(new ListViewItemsData()
{
InputStatus = "/Image/inputdisconnect.png",
InputName = "ASFWQEFAasdfqerwefc"
});
lv_input.ItemsSource = ListViewItemsCollections;
}
public class ListViewItemsData
{
public string InputStatus { get; set; }
public string InputName { get; set; }
}
}
Considering the example you've provided, it appears you have a typo on the 3rd line of your C# code. The property getter should be returning _ListViewItemsCollections like this:
public ObservableCollection<ListViewItemsData> ListViewItemsCollections { get { return _ListViewItemsCollections; } }
Also, I noticed that you are using ItemsSource directly, rather than setting the DataContext on the lv_input ListView. Whilst ItemsSource may work after fixing the typo, you may encounter unexpected behavior when trying to use item templates etc...
A more correct (but not perfect) way of doing what you are attempting would look like this:
public partial class IOSetPage : Page
{
IOSetPageView view;
public IOSetPage()
{
InitializeComponent();
view = new IOSetPageView();
view.Items.Add(new ListViewItemsData()
{
InputStatus = "/Image/inputdisconnect.png",
InputName = "ASFWQEFAasdfqerwefc"
});
lv_input.DataContext = view;
}
}
public class IOSetPageView
{
public ObservableCollection<ListViewItemsData> Items { get; }
= new ObservableCollection<ListViewItemsData>();
}
public class ListViewItemsData
{
public string InputStatus { get; set; }
public string InputName { get; set; }
}
With the XAML for the ListView looking like this:
<ListView Style="{StaticResource ListviewStyle}" ItemsSource="{Binding Items}" x:Name="lv_input">
<ListView.View>
<GridView>
<GridViewColumn Width="50" Header="InputStatus">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding InputStatus}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="200" Header="InputName"
DisplayMemberBinding="{Binding InputName}"/>
</GridView>
</ListView.View>
</ListView>
Notice how the ItemSource is now bound to the Items property of the view class I created. To improve further, you could implement the INotifyPropertyChanged interface for your ListViewItemsData class which would allow the ListView to update in real time when you modify a property.
I have a WPF form with some GridViewColumns in them to display the data. However, even though I set the Header property in each column, the headers never appear.
XAML:
<Window x:Class="MyProject.ViewModel.MyClassView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:MyProject.ViewModel"
Title="Title" Height="210.4" Width="500" ResizeMode="NoResize"
WindowStartupLocation="CenterOwner" WindowStyle="ToolWindow">
<Window.Resources>
<DataTemplate x:Key="tempA">
<TextBlock Text="{Binding Path=dataA}"/>
</DataTemplate>
<DataTemplate x:Key="tempB">
<TextBlock Text="{Binding Path=dataB}"/>
</DataTemplate>
<DataTemplate x:Key="tempC">
<TextBlock Text="{Binding Path=dataC}"/>
</DataTemplate>
</Window.Resources>
<Grid Margin="5,5,5,5">
<ListView x:Name="MyList" ItemsSource="{Binding MyDataCollection}" MouseDoubleClick="ListView_MouseDoubleClick">
<ListView.View>
<GridView>
<GridViewColumn x:Name="colA" Header="Name" Width="100" CellTemplate="{StaticResource tempA}"/>
<GridViewColumn x:Name="colB" Header="Type" Width="100" CellTemplate="{StaticResource tempB}"/>
<GridViewColumn x:Name="colC" Header="Diameter" Width="100" CellTemplate="{StaticResource tempC}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
Code-behind:
namespace MyProject.ViewModel
{
public partial class MyClassView : Window
{
public object SelectedItem = null;
public MyClassView()
{
InitializeComponent();
}
private void ListView_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
SelectedItem = MyList.SelectedItem;
}
}
}
ViewModel:
namespace MyProject.ViewModel
{
public class MyViewModel : INotifyPropertyChanged
{
public string Type { get; set; }
public object MyDataCollection { get; set; }
public MyViewModel(object collection, string type)
{
Type = type;
MyDataCollection = collection;
}
internal void RaisePropertyChanged(string prop)
{
if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); }
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
The object that is passed into the ViewModel constructor is guaranteed to contain dataA, dataB and dataC among others.
Could someone explain why the headers in my GridView aren't appearing?
Try using a DataGrid instead.
It would look something like this:
<DataGrid ItemsSource="{Binding MyDataCollection}" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" IsReadOnly="True"/>
<DataGridComboBoxColumn Header="Type" ItemsSource="{Binding TypeList}" SelectedItemBinding="{Binding Type}"/>
...
</DataGrid.Columns>
</DataGrid>
i have a listview template and one column is a button. I need selected item when i click in this button. How i can do this ??
To cature the selected ListView item inside a button pressed event you can leverage the MVVM pattern. In my ListView, in the XAML, I bind the ItemsSource and SelectedItem to a ViewModel class. I also bind my button Command in the template to RunCommand in the ViewModel.
The tricky part is getting the binding correct from the template to the active DataContext.
Once you do this you can capture the SelectedCustomer inside the RunCommand that
gets executed when the button gets pressed.
I've included some of the code to help get you started.
You can find implementations of ViewModelBase and DelegateCommand via Google.
Here is the XAML:
<Window x:Class="ListViewScrollPosition.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Main Window" Height="400" Width="400">
<Grid>
<ListView ItemsSource="{Binding Path=Customers}"
SelectedItem="{Binding Path=SelectedCustomer}"
Width="Auto">
<ListView.View>
<GridView>
<GridViewColumn Header="First Name">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Margin="6,2,6,2">
<TextBlock Text="{Binding FirstName}"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Last Name">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Margin="6,2,6,2">
<TextBlock Text="{Binding LastName}"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Address">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Margin="6,2,6,2">
<Button Content="Address"
Command="{Binding
Path=DataContext.RunCommand,
RelativeSource=
{RelativeSource FindAncestor,
AncestorType={x:Type ItemsControl}}}"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
Here is the ViewModel:
using System.Collections.ObjectModel;
using System.Windows.Input;
using ListViewScrollPosition.Commands;
using ListViewScrollPosition.Models;
namespace ListViewScrollPosition.ViewModels
{
public class MainViewModel : ViewModelBase
{
public ICommand RunCommand { get; private set; }
public MainViewModel()
{
RunCommand = new DelegateCommand<object>(OnRunCommand, CanRunCommand);
_customers = Customer.GetSampleCustomerList();
_selectedCustomer = _customers[0];
}
private ObservableCollection<Customer> _customers =
new ObservableCollection<Customer>();
public ObservableCollection<Customer> Customers
{
get
{
return _customers;
}
}
private Customer _selectedCustomer;
public Customer SelectedCustomer
{
get
{
return _selectedCustomer;
}
set
{
_selectedCustomer = value;
OnPropertyChanged("SelectedCustomer");
}
}
private void OnRunCommand(object obj)
{
// use the SelectedCustomer object here...
}
private bool CanRunCommand(object obj)
{
return true;
}
}
}
Here is where I link in the ViewModel to the View:
public partial class MainView : Window
{
public MainView()
{
InitializeComponent();
DataContext = new ViewModels.MainViewModel();
}
}
Example with a regular click event in the code behind:
<ListView Height="167.96" VerticalAlignment="Top" ItemsSource="{Binding FulfillmentSchedules}" SelectedItem="{Binding SelectedFulfillmentSchedule}">
<ListView.View>
<GridView>
<GridViewColumn Header="Request">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}-{1}-{2}">
<Binding Path="Template.ProjectNumber" />
<Binding Path="Template.JobNumber" />
<Binding Path="Template.RequestId" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Template" DisplayMemberBinding="{Binding Template.Name}"/>
<GridViewColumn Header="Start Date" DisplayMemberBinding="{Binding StartDate}"/>
<GridViewColumn Header="Records" DisplayMemberBinding="{Binding Parameters.Records}"/>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Name="BtnYourButton" Content="Your Button" Click="BtnYourButton_Click" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Code behind:
private void BtnYourButton_Click(object sender, RoutedEventArgs e)
{
var boundData= (YourBoundDataType)((Button)sender).DataContext;
//do what you need to do here, including calling other methods on your VM
}
Note: While I certainly appreciate MVVM, I've come to accept that there is a pretty steep slope of dimminishing returns once you cross into actions and messaging between the form and the VM, so I use it only in cases of complex relationships between VMs or large singular VMs. For CRUD style data-centric applications I prefer to handle actions and message relay with the code behind.
Ok the title maybe a little confusing. I have a database with the table Companies wich has one-to-many relotionship with another table Divisions ( so each company can have many divisions ) and division will have many employees.
I have a ListView of the companies. What I wan't is that when I choose a company from the ListView another ListView of divisions within that company appears below it. Then I pick a division and another listview of employees within that division appaers below that. You get the picture.
Is there anyway to do this mostly inside the XAML code declaritively (sp?). I'm using linq so the Company entity objects have a property named Division wich if I understand linq correctly should include Division objects of the divisions connected to the company. So after getting all the companies and putting them as a itemsource to CompanyListView this is where I currently am.
<ListView x:Name="CompanyListView"
DisplayMemberPath="CompanyName"
Grid.Row="0" Grid.Column="0" />
<ListView DataContext="{Binding ElementName=CompanyListView, Path=SelectedItem}"
DisplayMemberPath="Division.DivisionName"
Grid.Row="1" Grid.Column="0" />
I know I'm way off but I was hoping by putting something specific in the DataContext and DisplayMemberPath I could get this to work. If not then I have to capture the Id of the company I guess and capture a select event or something.
Another issue but related is the in the seconde column besides the lisview I wan't to have a details/edit view for the selected item. So when only a company is selected details about that will appear then when a division under the company is picked It will go there instead, any ideas?
You can use the MVVM pattern to bind your XAML to a class that contains information for your ListViews and reset the content for the Division collection based on the selected Comany item.
Here is a basic sample to get you started.
Here are two ListView controls in XAML:
<Window x:Class="MultiListView.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Main Window" Height="400" Width="800">
<DockPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ListView Grid.Row="0"
ItemsSource="{Binding Companies}"
SelectedItem="{Binding Company, Mode=TwoWay}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name"
DisplayMemberBinding="{Binding CompanyName}" />
<GridViewColumn Header="Description"
DisplayMemberBinding="{Binding Description}" />
</GridView>
</ListView.View>
</ListView>
<ListView Grid.Row="1"
ItemsSource="{Binding Divisions}"
SelectedItem="{Binding Division, Mode=TwoWay}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name"
DisplayMemberBinding="{Binding DivisionName}" />
<GridViewColumn Header="Description"
DisplayMemberBinding="{Binding Description}" />
</GridView>
</ListView.View>
</ListView>
</Grid>
</DockPanel>
</Window>
In the code-behind set the DataContext for the Window to a class the contains the binding references used in the XAML.
public partial class MainView : Window
{
MainViewModel _mvm = new MainViewModel();
public MainView()
{
InitializeComponent();
DataContext = _mvm;
}
}
The following class uses the MVVM pattern, which you can find lots of information
on in StackOverFlow. This class contains the data that the XAML binds with. Here
is where you can use LINQ to load/reload the collections.
using System.Collections.ObjectModel;
using MultiListView.Models;
namespace MultiListView.ViewModels
{
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
_companies = new ObservableCollection<Company>();
_companies.Add(new Company("Stackoverflow", "QA web site"));
_companies.Add(new Company("Fog Creek", "Agile Bug Tracking"));
_companies.Add(new Company("Second Beach", "Not sure yet"));
_divisions = new ObservableCollection<Division>();
}
private ObservableCollection<Company> _companies;
public ObservableCollection<Company> Companies
{
get { return _companies; }
set
{
_companies = value;
OnPropertyChanged("Companies");
}
}
private Company _company;
public Company Company
{
get { return _company; }
set
{
_company = value;
// load/reload divisions for the selected company here
LoadDivisions();
OnPropertyChanged("Company");
}
}
// hack to keep the example simpe...
private void LoadDivisions()
{
_divisions.Clear();
// use db or linq here to filiter property
if ( _company != null )
{
if ( _company.CompanyName.Equals("Stackoverflow") )
{
_divisions.Add( new Division("QA", "Test all day"));
_divisions.Add( new Division("Write", "Doc all day"));
_divisions.Add( new Division("Code", "Code all day"));
}
else if (_company.CompanyName.Equals("Fog Creek"))
{
_divisions.Add(new Division("Test", "Test all day"));
_divisions.Add(new Division("Doc", "Doc all day"));
_divisions.Add(new Division("Develop", "Code all day"));
}
else if (_company.CompanyName.Equals("Second Beach"))
{
_divisions.Add(new Division("Engineering", "Code all day"));
}
}
}
private ObservableCollection<Division> _divisions;
public ObservableCollection<Division> Divisions
{
get { return _divisions; }
set
{
_divisions = value;
OnPropertyChanged("Divisions");
}
}
private Division _division;
public Division Division
{
get { return _division; }
set
{
_division = value;
OnPropertyChanged("Division");
}
}
}
}
OnPropertyChanged implements INotifyPropertyChanged.
When the properties of a ViewModel change, the Views bound to the ViewModel receive a notification when the ViewModel raises its PropertyChanged event.
You can find examples in most MVVM libraries, or look to MSDN for an example.
If Divisions is a property of Company, you could probably do something like this:
<Window x:Class="MultiListView.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Main Window" Height="400" Width="800">
<DockPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ListView Grid.Row="0"
x:Name="lvCompanies"
ItemsSource="{Binding Companies}"
SelectedItem="{Binding Company, Mode=TwoWay}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name"
DisplayMemberBinding="{Binding CompanyName}" />
<GridViewColumn Header="Description"
DisplayMemberBinding="{Binding Description}" />
</GridView>
</ListView.View>
</ListView>
<ListView Grid.Row="1"
ItemsSource="{Binding ElementName='lvCompanies', Path=SelectedItem.Divisions}"
SelectedItem="{Binding Division, Mode=TwoWay}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name"
DisplayMemberBinding="{Binding DivisionName}" />
<GridViewColumn Header="Description"
DisplayMemberBinding="{Binding Description}" />
</GridView>
</ListView.View>
</ListView>
</Grid>
</DockPanel>
</Window>
I'm still learning WPF, but I'm really confused about something that should be really simple. What I want to do is to center the contents of the 3rd and 4th columns. When I run this, the columns are left justified:
<ListView Margin="0" x:Name="listMonitoredUrls" AlternationCount="1"
ItemsSource="{Binding}" >
<ListView.View>
<GridView>
<GridViewColumn Header="Description" DisplayMemberBinding="{Binding FriendlyDesc}"/>
<GridViewColumn Header="Url" DisplayMemberBinding="{Binding Url}"/>
<GridViewColumn Header="Frequency">
<GridViewColumn.CellTemplate >
<DataTemplate>
<TextBlock Text="{Binding ScanFrequencyMinutes}"
HorizontalAlignment="Center"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Next Scan" >
<GridViewColumn.CellTemplate >
<DataTemplate>
<TextBlock Text="{Binding TimeNextScanStr}"
HorizontalAlignment="Center"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
I"m really starting to like WPF, but some simple things like this seem to be really hard.
Try using the TextAlignment property instead of HorizontalAlignment - should do it.
To my understanding HorizontalAlignment="Center" will center your textblock not the text in it.
This might be a long shot but i've had to do it for listboxes where the items are defined by templates. Try setting the HorizontalContentAlignment="Stretch" on your ListView. If I don't set that the items take only as much space as they need and are left justified.
I've created a solution which works under the common scenario of:
<GridViewColumn Header="Some Property" DisplayMemberBinding="{Binding SomeProperty}" />
where one only wants a simple DisplayMemberBinding with text without having to specify a CellTemplate
the new code uses an attached property and becomes:
<GridViewColumn Header="Some Property" DisplayMemberBinding="{Binding SomeProperty}"
ctrl:GridViewExtensions.IsContentCentered="True" />
attached property code:
public static class GridViewExtensions
{
#region IsContentCentered
[Category("Common")]
[AttachedPropertyBrowsableForType(typeof(GridViewColumn))]
public static bool GetIsContentCentered(GridViewColumn gridViewColumn)
{
return (bool)gridViewColumn.GetValue(IsContentCenteredProperty);
}
public static void SetIsContentCentered(GridViewColumn gridViewColumn, bool value)
{
gridViewColumn.SetValue(IsContentCenteredProperty, value);
}
public static readonly DependencyProperty IsContentCenteredProperty =
DependencyProperty.RegisterAttached(
"IsContentCentered",
typeof(bool), // type
typeof(GridViewExtensions), // containing type
new PropertyMetadata(default(bool), OnIsContentCenteredChanged)
);
private static void OnIsContentCenteredChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
OnIsContentCenteredChanged((GridViewColumn)d, (bool)e.NewValue);
}
private static void OnIsContentCenteredChanged(GridViewColumn gridViewColumn, bool isContentCentered)
{
if (isContentCentered == false) { return; }
// must wait a bit otherwise GridViewColumn.DisplayMemberBinding will not yet be initialized,
new DispatcherTimer(TimeSpan.FromMilliseconds(100), DispatcherPriority.Normal, OnColumnLoaded, gridViewColumn.Dispatcher)
{
Tag = gridViewColumn
}.Start();
}
static void OnColumnLoaded(object sender, EventArgs e)
{
var timer = (DispatcherTimer)sender;
timer.Stop();
var gridViewColumn = (GridViewColumn)timer.Tag;
if (gridViewColumn.DisplayMemberBinding == null)
{
throw new Exception("Only allowed with DisplayMemberBinding.");
}
var textBlockFactory = new FrameworkElementFactory(typeof(TextBlock));
textBlockFactory.SetBinding(TextBlock.TextProperty, gridViewColumn.DisplayMemberBinding);
textBlockFactory.SetValue(TextBlock.TextAlignmentProperty, TextAlignment.Center);
var cellTemplate = new DataTemplate { VisualTree = textBlockFactory };
gridViewColumn.DisplayMemberBinding = null; // must null, otherwise CellTemplate won't be recognized
gridViewColumn.CellTemplate = cellTemplate;
}
#endregion IsContentCentered
}
Here is my example to show a working xaml:
<Window x:Class="WPF_Tutorial.Rich_text_controls.BlockUIContainerCenteredColumnSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:self="clr-namespace:WPF_Tutorial.Rich_text_controls"
Title="BlockUIContainerCenteredColumnSample" Height="275" Width="300"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<x:Array x:Key="UserArray" Type="{x:Type self:User}">
<self:User Name="John Doe" Age="42" />
<self:User Name="Jane May-Anne Josephine Renalds Doe" Age="36" />
</x:Array>
</Window.Resources>
<Grid>
<FlowDocumentScrollViewer>
<FlowDocument>
<Paragraph FontSize="36" Margin="0">Users</Paragraph>
<Paragraph FontStyle="Italic" TextAlignment="Left" FontSize="14" Foreground="Gray">Here's a list of our users, inside our FlowDocument, in a completely interactive ListView control!</Paragraph>
<BlockUIContainer>
<ListView BorderThickness="0" ItemsSource="{StaticResource UserArray}" HorizontalAlignment="Center">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<!-- This stretches out the TextBlock width to the column width -->
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="150" />
<GridViewColumn>
<GridViewColumnHeader Content="Age" Width="75" />
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Age}" TextAlignment="Center" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</BlockUIContainer>
<Paragraph FontStyle="Italic" TextAlignment="Left" FontSize="14" Foreground="Gray">More content can go here...</Paragraph>
</FlowDocument>
</FlowDocumentScrollViewer>
</Grid>
</Window>
Notice the <ListView.ItemContainerStyle> block. It has the <Setter ....
Without this, as per AndyG's text, nothing will work the way you want.
This has been very frustrating trying to work out.
By the way, here is the backing-code for this xaml:
namespace WPF_Tutorial.Rich_text_controls
{
using System.Windows;
public partial class BlockUIContainerCenteredColumnSample : Window
{
public BlockUIContainerCenteredColumnSample()
{
InitializeComponent();
}
}
public class User
{
public int Age { get; set; }
public string Name { get; set; }
}
}
What you should see when run