问题
I've created a little wpf test project to demonstrate my question. This project contains a single wpf window. This window contains only a TabControl
. The pages of this TabControl
are created dynamically from the bound ItemSource
.
My XAML:
<Window x:Name="window" x:Class="TestWpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:TestWpf"
Title="MainWindow" Height="350" Width="525">
<TabControl x:Name="tabControl" BorderThickness="0" ItemsSource ="{Binding MediaLists, ElementName=window, NotifyOnSourceUpdated=True}">
<TabControl.ItemTemplate>
<DataTemplate DataType="{x:Type vm:MediaList}">
<TextBlock Padding="2" Text="{Binding MediaTypeName}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate x:Name="contentTemplate" DataType="{x:Type vm:MediaList}">
<DataGrid x:Name="dgMediaList" ItemsSource="{Binding Medias, BindsDirectlyToSource=True}" DockPanel.Dock="Top" AutoGenerateColumns="False" HorizontalGridLinesBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}" VerticalGridLinesBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}">
<DataGrid.Columns>
<DataGridTextColumn x:Name="clmAuthor" Header="Author" Binding="{Binding Author}" IsReadOnly="True" CanUserReorder="False" />
<DataGridTextColumn x:Name="clmTitle" Header="Title" Binding="{Binding Title}" IsReadOnly="True" CanUserReorder="False" />
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Window>
And the code behind is like this:
public partial class MainWindow : Window
{
private readonly ObservableCollection<MediaList> m_MediaLists = new ObservableCollection<MediaList>();
public ObservableCollection<MediaList> MediaLists { get { return m_MediaLists; } }
public MainWindow()
{
InitializeComponent();
MediaList cdList = new MediaList("CDs");
cdList.Medias.Add(new Media("AuthorCdA", "TitleCdA1"));
cdList.Medias.Add(new Media("AuthorCdA", "TitleCdA2"));
cdList.Medias.Add(new Media("AuthorCdB", "TitleCdB1"));
cdList.Medias.Add(new Media("AuthorCdB", "TitleCdB2"));
MediaList bookList = new MediaList("Books");
bookList.Medias.Add(new Media("AuthorBookA", "TitleBookA1"));
bookList.Medias.Add(new Media("AuthorBookA", "TitleBookA2"));
bookList.Medias.Add(new Media("AuthorBookB", "TitleBookB1"));
bookList.Medias.Add(new Media("AuthorBookB", "TitleBookB2"));
m_MediaLists.Add(cdList);
m_MediaLists.Add(bookList);
}
}
With MediaList
as this:
public class MediaList : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
private readonly ObservableCollection<Media> m_Medias = new ObservableCollection<Media>();
public string MediaTypeName { get; private set; }
public ObservableCollection<Media> Medias { get { return m_Medias; }}
public MediaList(string typeName)
{
MediaTypeName = typeName;
}
}
And Media
like this:
public class Media
{
public string Author { get; private set; }
public string Title { get; private set; }
public Media(string author, string title)
{
Author = author;
Title = title;
}
}
So this all works fine as you can see here:
Now the problem: is how to save the DataGrid
's layout for each TabPage
? When I switch between the TabPage
s, the column width are kept the same for all pages, but the sorting is always lost completely.
I want to bind the column widths of the DataGrid
as well as the multi-column sort settings as changed by the user in members of the MediaList
instance.
So to keep this question short, let's concentrate on the column widths. I added the members to the MediaList
class:
private DataGridLength m_WidthAuthor = DataGridLength.SizeToCells;
private DataGridLength m_WidthTitle = DataGridLength.SizeToCells;
public DataGridLength WidthAuthor
{
get { return m_WidthAuthor; }
set
{
if (value == m_WidthAuthor) return;
m_WidthAuthor = value;
OnPropertyChanged("WidthAuthor");
}
}
public DataGridLength WidthTitle
{
get { return m_WidthTitle; }
set
{
if (value == m_WidthTitle) return;
m_WidthTitle = value;
OnPropertyChanged("WidthTitle");
}
}
and tried to set the binding in the xaml:
<DataGridTextColumn x:Name="clmAuthor" ... Width="{Binding WidthAuthor, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}" />
<DataGridTextColumn x:Name="clmTitle" ... Width="{Binding WidthTitle, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}" />
But unfortunatly, this doesn't work. I've read on several SO articles that a two-way binding would be difficult. But even one-way binding won't work for me. (I'm pretty new to wpf/mvvm, so maybe I use some wrong words here...)
In the debugger output window I can see these error message for my width-bindings:
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=WidthAuthor; DataItem=null; target element is 'DataGridTextColumn' (HashCode=54916642); target property is 'Width' (type 'DataGridLength')
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=WidthTitle; DataItem=null; target element is 'DataGridTextColumn' (HashCode=40809782); target property is 'Width' (type 'DataGridLength')
So if anyone can tell me how to save those DataGrid
properties for each TabPage
I would be very happy. Focus of this question is on the widths. The sorting may be subject of a new question (note that binding the SortDirection
of the columns probably won't work for multi-column sort)
EDIT: I've added diag:PresentationTraceSources.TraceLevel=High
to the Binding
expression to one of the columns Width
and found these messages in the debug output:
System.Windows.Data Warning: 62 : BindingExpression (hash=18270086): Attach to System.Windows.Controls.DataGridTextColumn.Width (hash=37671782)
System.Windows.Data Warning: 64 : BindingExpression (hash=18270086): Use Framework mentor <null>
System.Windows.Data Warning: 67 : BindingExpression (hash=18270086): Resolving source
System.Windows.Data Warning: 69 : BindingExpression (hash=18270086): Framework mentor not found
System.Windows.Data Warning: 65 : BindingExpression (hash=18270086): Resolve source deferred
I'm too new to wpf to understand what that Framework mentor
is and if this should really be null
.
回答1:
I solved it with the help of this article. Unfortunately my wpf knowledge is still so low that I cannot explain (as well as the author of the mentioned article) what happens behind the scenes and if this is really a good solution.
So here is what I did:
Implementing a `BindingProxy` inherited from `Freezable`
One of the problems is that the DataContext
is not inherited to DataGridTextColumn
. So the aim is to store it in a proxy.
public class BindingProxy : Freezable
{
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
public object Data
{
get { return GetValue(DATA_PROPERTY); }
set { SetValue(DATA_PROPERTY, value); }
}
// Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DATA_PROPERTY =
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
Declaring an instance of `BindingProxy` as local resource in `DataGrid`
I added this local resource to my DataGrid
in xaml:
<DataGrid.Resources>
<vm:BindingProxy x:Key="proxy" Data="{Binding}" />
</DataGrid.Resources>
Adjusting the binding
At last I modify the binding expression for the DataGridTextColum
's width:
Width="{Binding Data.WidthAuthor, Source={StaticResource proxy}....
et voilá, it works!
I will continue research on how to do this for the multi-column sort. If I can't find a solution to that I will open a new question. If no one adds a solution for that in the next days, I will accept my own answer.
UPDATE: I solved the multi-column sort problem here.
来源:https://stackoverflow.com/questions/34661767/binding-properties-width-sorting-of-datagrid-on-dynamic-tabpage