【问题标题】:Binding properties (width/sorting) of DataGrid on dynamic TabPage动态 TabPage 上 DataGrid 的绑定属性(宽度/排序)
【发布时间】:2016-01-07 17:44:19
【问题描述】:

我创建了一个小 wpf 测试项目来演示我的问题。该项目包含一个 wpf 窗口。此窗口仅包含TabControl。这个TabControl的页面是从绑定的ItemSource动态创建的。

我的 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>

而后面的代码是这样的:

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

MediaList 是这样的:

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

Media 这样:

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

所以这一切都很好,你可以在这里看到:

现在的问题是:如何为每个TabPage 保存DataGrid 的布局? 当我在TabPages 之间切换时,所有的列宽都保持不变页面,但排序总是完全丢失。

我想绑定DataGrid 的列宽以及用户在MediaList 实例成员中更改的多列 排序设置。

所以为了让这个问题简短,让我们专注于列宽。我将成员添加到MediaList 类:

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

并尝试在 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}" />

但不幸的是,这不起作用。我已经阅读了几篇 SO 文章,说明双向绑定很困难。但即使是单向绑定也不适合我。 (我对 wpf/mvvm 还很陌生,所以我可能在这里用了一些错误的词......

在调试器输出窗口中,我可以看到我的宽度绑定错误消息:

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

因此,如果有人能告诉我如何为每个 TabPage 保存这些 DataGrid 属性,我将非常高兴。这个问题的重点是宽度。排序可能是一个新问题的主题(请注意,绑定列的SortDirection 可能不适用于多列排序)

编辑:我已将diag:PresentationTraceSources.TraceLevel=High 添加到Binding 表达式到Width 列之一,并在调试输出中找到这些消息:

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

我对 wpf 还太陌生,无法理解 Framework mentor 是什么以及这是否真的应该是 null

【问题讨论】:

    标签: c# wpf xaml datagrid tabcontrol


    【解决方案1】:

    我在this article 的帮助下解决了这个问题。不幸的是,我的 wpf 知识仍然很低,以至于我无法解释(以及所提到文章的作者)幕后发生的事情以及这是否真的是一个好的解决方案。

    这就是我所做的:

    实现从 `Freezable` 继承的 `BindingProxy`

    其中一个问题是DataContext 没有继承到DataGridTextColumn。所以目的是将其存储在代理中。

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

    将 `BindingProxy` 的实例声明为 `DataGrid` 中的本地资源

    我在 xaml 中将此本地资源添加到我的 DataGrid

    <DataGrid.Resources>
        <vm:BindingProxy x:Key="proxy" Data="{Binding}" />
    </DataGrid.Resources>
    

    调整绑定

    最后我修改了DataGridTextColum宽度的绑定表达式:

    Width="{Binding Data.WidthAuthor, Source={StaticResource proxy}....
    

    瞧,它的工作原理!

    我将继续研究如何为多列排序执行此操作。如果我找不到解决方案,我将提出一个新问题。如果接下来几天没有人为此添加解决方案,我将接受我自己的答案。

    更新:我解决了多列排序问题here

    【讨论】:

      猜你喜欢
      • 2011-11-29
      • 2011-12-27
      • 1970-01-01
      • 2011-01-18
      • 2016-10-28
      • 1970-01-01
      • 1970-01-01
      • 2014-08-15
      • 2011-10-17
      相关资源
      最近更新 更多